Coverage for dataexcept/datascience_exceptions/operations.py: 78%
64 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-03 20:46 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-03 20:46 +0000
1"""Operational and deployment errors for ML systems."""
3from __future__ import annotations
5from typing import Any, Optional
7from .._validation import is_number
8from ..redaction import redact_if_url
9from .base import DataScienceError
12class ModelSerializationError(DataScienceError):
13 """
14 Raised when saving or loading a model fails.
16 Attributes:
17 path: file path involved.
18 original: underlying exception.
19 """
21 def __init__(self, path: str, original: Exception) -> None:
22 if not isinstance(path, str): 22 ↛ 23line 22 didn't jump to line 23 because the condition on line 22 was never true
23 raise TypeError(f"path must be str, got {type(path).__name__}")
24 if not isinstance(original, Exception): 24 ↛ 25line 24 didn't jump to line 25 because the condition on line 24 was never true
25 raise TypeError(
26 f"original must be Exception, got {type(original).__name__}"
27 )
29 message = f"Failed to serialize to {path!r}: {original}"
30 self.path = redact_if_url(path)
31 self.original = original
32 super().__init__(message)
34 def __str__(self) -> str:
35 return f"[ModelSerializationError:{self.path}] {self.message}"
38class DeploymentError(DataScienceError):
39 """
40 Raised when deploying a model or pipeline fails.
42 Attributes:
43 target: deployment target identifier.
44 cause: optional detail.
45 """
47 def __init__(self, target: str, cause: Optional[str] = None) -> None:
48 if not isinstance(target, str): 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true
49 raise TypeError(f"target must be str, got {type(target).__name__}")
50 if cause is not None and not isinstance(cause, str): 50 ↛ 51line 50 didn't jump to line 51 because the condition on line 50 was never true
51 raise TypeError(f"cause must be str or None, got {type(cause).__name__}")
53 msg = f"Deployment failed to '{target}'"
54 if cause:
55 msg += f": {cause}"
57 self.target = target
58 self.cause = cause
59 super().__init__(msg)
61 def __str__(self) -> str:
62 return f"[DeploymentError:{self.target}] {self.message}"
65class DataDriftError(DataScienceError):
66 """
67 Raised when data drift is detected beyond threshold.
69 Attributes:
70 feature: feature name.
71 drift_score: computed drift metric.
72 """
74 def __init__(
75 self, feature: str, drift_score: float, message: Optional[str] = None
76 ) -> None:
77 if not isinstance(feature, str): 77 ↛ 78line 77 didn't jump to line 78 because the condition on line 77 was never true
78 raise TypeError(f"feature must be str, got {type(feature).__name__}")
79 if not is_number(drift_score): 79 ↛ 80line 79 didn't jump to line 80 because the condition on line 79 was never true
80 raise TypeError(
81 f"drift_score must be number, got {type(drift_score).__name__}"
82 )
84 self.feature = feature
85 self.drift_score = float(drift_score)
86 if message is None: 86 ↛ 89line 86 didn't jump to line 89 because the condition on line 86 was always true
87 message = f"Data drift detected on '{feature}', score={drift_score:.4f}"
89 super().__init__(message)
91 def __str__(self) -> str:
92 return f"[DataDriftError:{self.feature}] {self.message}"
95class ResourceLimitError(DataScienceError):
96 """
97 Raised when computation exceeds resources (memory, CPU).
99 Attributes:
100 resource: 'memory', 'cpu', etc.
101 limit: threshold exceeded.
102 """
104 def __init__(self, resource: str, limit: Any) -> None:
105 if not isinstance(resource, str): 105 ↛ 106line 105 didn't jump to line 106 because the condition on line 105 was never true
106 raise TypeError(f"resource must be str, got {type(resource).__name__}")
108 message = f"Resource limit exceeded: {resource} at {limit!r}"
109 self.resource = resource
110 self.limit = limit
111 super().__init__(message)
113 def __str__(self) -> str:
114 return f"[ResourceLimitError:{self.resource}] {self.message}"
117class DataExportError(DataScienceError):
118 """Failed to export or write data to destination."""
120 def __init__(self, destination: str, original: Exception) -> None:
121 if not isinstance(destination, str): 121 ↛ 122line 121 didn't jump to line 122 because the condition on line 121 was never true
122 raise TypeError(
123 f"destination must be str, got {type(destination).__name__}"
124 )
125 if not isinstance(original, Exception): 125 ↛ 126line 125 didn't jump to line 126 because the condition on line 125 was never true
126 raise TypeError(
127 f"original must be Exception, got {type(original).__name__}"
128 )
129 msg = f"Unable to export data to {destination}: {original}"
130 self.destination = destination
131 self.original = original
132 super().__init__(msg)