DataExcept 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,312 @@
1
+ """Data ingestion and validation related errors."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional, Sequence
6
+
7
+ from .base import DataScienceError
8
+
9
+
10
+ class DataLoadingError(DataScienceError):
11
+ """
12
+ Raised when loading data fails.
13
+
14
+ Attributes:
15
+ source: data source description (file path, URL).
16
+ original: underlying exception.
17
+ """
18
+
19
+ def __init__(self, source: str, original: Exception) -> None:
20
+ if not isinstance(source, str):
21
+ raise TypeError(f"source must be str, got {type(source).__name__}")
22
+ if not isinstance(original, Exception):
23
+ raise TypeError(
24
+ f"original must be Exception, got {type(original).__name__}"
25
+ )
26
+
27
+ message = f"Failed to load data from {source!r}: {original}"
28
+ self.source = source
29
+ self.original = original
30
+ super().__init__(message)
31
+
32
+ def __str__(self) -> str:
33
+ return f"[DataLoadingError] {self.source}"
34
+
35
+
36
+ class DataFormatError(DataScienceError):
37
+ """Raised when input data is not in the expected format."""
38
+
39
+ def __init__(self, expected_formats: Sequence[str], found_format: str) -> None:
40
+ if not isinstance(found_format, str):
41
+ raise TypeError(
42
+ f"found_format must be str, got {type(found_format).__name__}"
43
+ )
44
+ if not isinstance(expected_formats, Sequence) or isinstance(
45
+ expected_formats, str
46
+ ):
47
+ raise TypeError("expected_formats must be a sequence of strings")
48
+ if not all(isinstance(fmt, str) for fmt in expected_formats):
49
+ raise TypeError("expected_formats must contain strings")
50
+
51
+ self.expected_formats = list(expected_formats)
52
+ self.found_format = found_format
53
+ fmt_list = ", ".join(self.expected_formats)
54
+ message = f"Expected data format {fmt_list}; got {found_format}"
55
+ super().__init__(message)
56
+
57
+ def __str__(self) -> str:
58
+ return f"[DataFormatError] {self.message}"
59
+
60
+
61
+ class DataValidationError(DataScienceError):
62
+ """
63
+ Raised when data fails validation rules.
64
+
65
+ Attributes:
66
+ field: name of invalid field.
67
+ value: the invalid value.
68
+ """
69
+
70
+ def __init__(self, field: str, value: Any, message: Optional[str] = None) -> None:
71
+ if not isinstance(field, str):
72
+ raise TypeError(f"field must be str, got {type(field).__name__}")
73
+
74
+ if message is None:
75
+ message = f"Invalid value for '{field}': {value!r}"
76
+ elif not isinstance(message, str):
77
+ raise TypeError(f"message must be str, got {type(message).__name__}")
78
+
79
+ self.field = field
80
+ self.value = value
81
+ super().__init__(message)
82
+
83
+ def __str__(self) -> str:
84
+ return f"[DataValidationError:{self.field}] {self.message}"
85
+
86
+
87
+ class MissingDataError(DataScienceError):
88
+ """
89
+ Raised when required data is missing.
90
+
91
+ Attributes:
92
+ feature: name of missing feature.
93
+ """
94
+
95
+ def __init__(self, feature: str, message: Optional[str] = None) -> None:
96
+ if not isinstance(feature, str):
97
+ raise TypeError(f"feature must be str, got {type(feature).__name__}")
98
+
99
+ if message is None:
100
+ message = f"Missing required feature: {feature!r}"
101
+ elif not isinstance(message, str):
102
+ raise TypeError(f"message must be str, got {type(message).__name__}")
103
+
104
+ self.feature = feature
105
+ super().__init__(message)
106
+
107
+ def __str__(self) -> str:
108
+ return f"[MissingDataError] {self.feature}"
109
+
110
+
111
+ class OutlierDetectionError(DataScienceError):
112
+ """
113
+ Raised when outlier detection fails.
114
+
115
+ Attributes:
116
+ method: detection method name.
117
+ details: optional extra info.
118
+ """
119
+
120
+ def __init__(self, method: str, details: Optional[str] = None) -> None:
121
+ if not isinstance(method, str):
122
+ raise TypeError(f"method must be str, got {type(method).__name__}")
123
+ if details is not None and not isinstance(details, str):
124
+ raise TypeError(
125
+ f"details must be str or None, got {type(details).__name__}"
126
+ )
127
+
128
+ msg = f"Outlier detection failed using method '{method}'"
129
+ if details:
130
+ msg += f": {details}"
131
+
132
+ self.method = method
133
+ self.details = details
134
+ super().__init__(msg)
135
+
136
+ def __str__(self) -> str:
137
+ return f"[OutlierDetectionError:{self.method}] {self.message}"
138
+
139
+
140
+ class SchemaMismatchError(DataScienceError):
141
+ """
142
+ Raised when data schema does not match expected.
143
+
144
+ Attributes:
145
+ expected: expected schema description.
146
+ found: actual schema description.
147
+ """
148
+
149
+ def __init__(self, expected: str, found: str) -> None:
150
+ if not isinstance(expected, str):
151
+ raise TypeError(f"expected must be str, got {type(expected).__name__}")
152
+ if not isinstance(found, str):
153
+ raise TypeError(f"found must be str, got {type(found).__name__}")
154
+
155
+ message = f"Schema mismatch. Expected: {expected}, Found: {found}"
156
+ self.expected = expected
157
+ self.found = found
158
+ super().__init__(message)
159
+
160
+ def __str__(self) -> str:
161
+ return f"[SchemaMismatchError] {self.message}"
162
+
163
+
164
+ class FeatureEngineeringError(DataScienceError):
165
+ """
166
+ Raised during feature engineering steps.
167
+
168
+ Attributes:
169
+ step: description of the step that failed.
170
+ cause: optional underlying reason.
171
+ """
172
+
173
+ def __init__(self, step: str, cause: Optional[str] = None) -> None:
174
+ if not isinstance(step, str):
175
+ raise TypeError(f"step must be str, got {type(step).__name__}")
176
+ if cause is not None and not isinstance(cause, str):
177
+ raise TypeError(f"cause must be str or None, got {type(cause).__name__}")
178
+
179
+ msg = f"Feature engineering failed at step '{step}'"
180
+ if cause:
181
+ msg += f": {cause}"
182
+
183
+ self.step = step
184
+ self.cause = cause
185
+ super().__init__(msg)
186
+
187
+ def __str__(self) -> str:
188
+ return f"[FeatureEngineeringError] {self.message}"
189
+
190
+
191
+ class DataNormalizationError(DataScienceError):
192
+ """Raised when data normalization fails.
193
+
194
+ Args:
195
+ method: Normalization technique identifier.
196
+ details: Optional explanation of the failure.
197
+ """
198
+
199
+ def __init__(self, method: str, details: Optional[str] = None) -> None:
200
+ if not isinstance(method, str):
201
+ raise TypeError(f"method must be str, got {type(method).__name__}")
202
+ if details is not None and not isinstance(details, str):
203
+ raise TypeError(
204
+ f"details must be str or None, got {type(details).__name__}"
205
+ )
206
+ # Build a helpful error message
207
+ msg = f"Normalization using '{method}' failed"
208
+ if details:
209
+ msg += f": {details}"
210
+ self.method = method
211
+ self.details = details
212
+ super().__init__(msg)
213
+
214
+ def __str__(self) -> str:
215
+ return f"[DataNormalizationError:{self.method}] {self.message}"
216
+
217
+
218
+ class DataImbalanceError(DataScienceError):
219
+ """Raised when class distribution is too imbalanced.
220
+
221
+ Args:
222
+ ratio: Observed minority-to-majority ratio.
223
+ threshold: Minimum acceptable ratio.
224
+ message: Optional custom error message.
225
+ """
226
+
227
+ def __init__(
228
+ self, ratio: float, threshold: float, message: Optional[str] = None
229
+ ) -> None:
230
+ if not isinstance(ratio, (int, float)):
231
+ raise TypeError(f"ratio must be numeric, got {type(ratio).__name__}")
232
+ if not isinstance(threshold, (int, float)):
233
+ raise TypeError(
234
+ f"threshold must be numeric, got {type(threshold).__name__}"
235
+ )
236
+ if message is not None and not isinstance(message, str):
237
+ raise TypeError(
238
+ f"message must be str or None, got {type(message).__name__}"
239
+ )
240
+ self.ratio = float(ratio)
241
+ self.threshold = float(threshold)
242
+ if message is None:
243
+ msg = (
244
+ f"Data imbalance detected: ratio={self.ratio:.3f} < "
245
+ f"threshold={self.threshold:.3f}"
246
+ )
247
+ else:
248
+ msg = message
249
+ super().__init__(msg)
250
+
251
+ def __str__(self) -> str:
252
+ return f"[DataImbalanceError] {self.message}"
253
+
254
+
255
+ class DataAugmentationError(DataScienceError):
256
+ """Raised when a data augmentation technique fails.
257
+
258
+ Args:
259
+ technique: Name of the augmentation technique.
260
+ details: Optional explanation of the failure.
261
+ """
262
+
263
+ def __init__(self, technique: str, details: Optional[str] = None) -> None:
264
+ if not isinstance(technique, str):
265
+ raise TypeError(f"technique must be str, got {type(technique).__name__}")
266
+ if details is not None and not isinstance(details, str):
267
+ raise TypeError(
268
+ f"details must be str or None, got {type(details).__name__}"
269
+ )
270
+
271
+ msg = f"Data augmentation '{technique}' failed"
272
+ if details:
273
+ msg += f": {details}"
274
+
275
+ self.technique = technique
276
+ self.details = details
277
+ super().__init__(msg)
278
+
279
+ def __str__(self) -> str:
280
+ return f"[DataAugmentationError:{self.technique}] {self.message}"
281
+
282
+
283
+ class DataLeakageError(DataScienceError):
284
+ """Raised when data leakage is detected between train and test sets.
285
+
286
+ Args:
287
+ feature: Name of the leaked feature.
288
+ stage: Stage where the leakage occurred.
289
+ message: Optional custom message.
290
+ """
291
+
292
+ def __init__(self, feature: str, stage: str, message: Optional[str] = None) -> None:
293
+ if not isinstance(feature, str):
294
+ raise TypeError(f"feature must be str, got {type(feature).__name__}")
295
+ if not isinstance(stage, str):
296
+ raise TypeError(f"stage must be str, got {type(stage).__name__}")
297
+ if message is not None and not isinstance(message, str):
298
+ raise TypeError(
299
+ f"message must be str or None, got {type(message).__name__}"
300
+ )
301
+
302
+ if message is None:
303
+ msg = f"Data leakage detected for '{feature}' during {stage}"
304
+ else:
305
+ msg = message
306
+
307
+ self.feature = feature
308
+ self.stage = stage
309
+ super().__init__(msg)
310
+
311
+ def __str__(self) -> str:
312
+ return f"[DataLeakageError:{self.feature}] {self.message}"
@@ -0,0 +1,130 @@
1
+ """Operational and deployment errors for ML systems."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from .base import DataScienceError
8
+
9
+
10
+ class SerializationError(DataScienceError):
11
+ """
12
+ Raised when saving or loading a model fails.
13
+
14
+ Attributes:
15
+ path: file path involved.
16
+ original: underlying exception.
17
+ """
18
+
19
+ def __init__(self, path: str, original: Exception) -> None:
20
+ if not isinstance(path, str):
21
+ raise TypeError(f"path must be str, got {type(path).__name__}")
22
+ if not isinstance(original, Exception):
23
+ raise TypeError(
24
+ f"original must be Exception, got {type(original).__name__}"
25
+ )
26
+
27
+ message = f"Failed to serialize to {path!r}: {original}"
28
+ self.path = path
29
+ self.original = original
30
+ super().__init__(message)
31
+
32
+ def __str__(self) -> str:
33
+ return f"[SerializationError] {self.path}"
34
+
35
+
36
+ class DeploymentError(DataScienceError):
37
+ """
38
+ Raised when deploying a model or pipeline fails.
39
+
40
+ Attributes:
41
+ target: deployment target identifier.
42
+ cause: optional detail.
43
+ """
44
+
45
+ def __init__(self, target: str, cause: Optional[str] = None) -> None:
46
+ if not isinstance(target, str):
47
+ raise TypeError(f"target must be str, got {type(target).__name__}")
48
+ if cause is not None and not isinstance(cause, str):
49
+ raise TypeError(f"cause must be str or None, got {type(cause).__name__}")
50
+
51
+ msg = f"Deployment failed to '{target}'"
52
+ if cause:
53
+ msg += f": {cause}"
54
+
55
+ self.target = target
56
+ self.cause = cause
57
+ super().__init__(msg)
58
+
59
+ def __str__(self) -> str:
60
+ return f"[DeploymentError] {self.target}"
61
+
62
+
63
+ class DataDriftError(DataScienceError):
64
+ """
65
+ Raised when data drift is detected beyond threshold.
66
+
67
+ Attributes:
68
+ feature: feature name.
69
+ drift_score: computed drift metric.
70
+ """
71
+
72
+ def __init__(
73
+ self, feature: str, drift_score: float, message: Optional[str] = None
74
+ ) -> None:
75
+ if not isinstance(feature, str):
76
+ raise TypeError(f"feature must be str, got {type(feature).__name__}")
77
+ if not isinstance(drift_score, (int, float)):
78
+ raise TypeError(
79
+ f"drift_score must be number, got {type(drift_score).__name__}"
80
+ )
81
+
82
+ self.feature = feature
83
+ self.drift_score = float(drift_score)
84
+ if message is None:
85
+ message = f"Data drift detected on '{feature}', score={drift_score:.4f}"
86
+
87
+ super().__init__(message)
88
+
89
+ def __str__(self) -> str:
90
+ return f"[DataDriftError:{self.feature}] {self.message}"
91
+
92
+
93
+ class ResourceLimitError(DataScienceError):
94
+ """
95
+ Raised when computation exceeds resources (memory, CPU).
96
+
97
+ Attributes:
98
+ resource: 'memory', 'cpu', etc.
99
+ limit: threshold exceeded.
100
+ """
101
+
102
+ def __init__(self, resource: str, limit: Any) -> None:
103
+ if not isinstance(resource, str):
104
+ raise TypeError(f"resource must be str, got {type(resource).__name__}")
105
+
106
+ message = f"Resource limit exceeded: {resource} at {limit!r}"
107
+ self.resource = resource
108
+ self.limit = limit
109
+ super().__init__(message)
110
+
111
+ def __str__(self) -> str:
112
+ return f"[ResourceLimitError:{self.resource}] {self.message}"
113
+
114
+
115
+ class DataExportError(DataScienceError):
116
+ """Failed to export or write data to destination."""
117
+
118
+ def __init__(self, destination: str, original: Exception) -> None:
119
+ if not isinstance(destination, str):
120
+ raise TypeError(
121
+ f"destination must be str, got {type(destination).__name__}"
122
+ )
123
+ if not isinstance(original, Exception):
124
+ raise TypeError(
125
+ f"original must be Exception, got {type(original).__name__}"
126
+ )
127
+ msg = f"Unable to export data to {destination}: {original}"
128
+ self.destination = destination
129
+ self.original = original
130
+ super().__init__(msg)