Source code for calibrated_explanations.utils.exceptions

"""Custom exception hierarchy for calibrated_explanations.

These exceptions standardize error signaling across the library.
Moved from core to utils to avoid circular dependencies (ADR-001).

ADR-002 compliance: All exceptions inherit from CalibratedError and support
structured error payloads via the ``details`` kwarg.
"""

from __future__ import annotations

from typing import Any

__all__ = [
    "CalibratedError",
    "ValidationError",
    "DataShapeError",
    "ConfigurationError",
    "ModelNotSupportedError",
    "NotFittedError",
    "ConvergenceError",
    "SerializationError",
    "IncompatibleStateError",
    "PlotPluginError",
    "MissingExtensionError",
    "explain_exception",
]


[docs] class CalibratedError(Exception): """Base class for library-specific errors.""" def __init__(self, message: str, *, details: dict[str, Any] | None = None) -> None: """Attach structured error details alongside the user-facing message.""" super().__init__(message) self.details: dict[str, Any] | None = details def __repr__(self) -> str: # pragma: no cover - repr stability check in tests """Return the exception representation with the message payload.""" cls = self.__class__.__name__ return f"{cls}({super().__str__()!r})"
[docs] class ValidationError(CalibratedError): """Inputs or configuration failed validation."""
[docs] class DataShapeError(ValidationError): """Provided data has incompatible shape or dtype (e.g., x/y mismatch)."""
[docs] class ConfigurationError(CalibratedError): """Invalid or conflicting configuration/parameter combination."""
[docs] class ModelNotSupportedError(CalibratedError): """Unsupported model type or missing required methods for task (e.g., predict_proba)."""
[docs] class NotFittedError(CalibratedError): """Operation requires a fitted estimator/explainer."""
[docs] class ConvergenceError(CalibratedError): """Optimization or calibration failed to converge within limits."""
[docs] class SerializationError(CalibratedError): """Failed to serialize/deserialize explanation artifacts."""
class IncompatibleStateError(SerializationError): """Persisted state could not be loaded due to schema or checksum mismatch."""
[docs] def explain_exception(e: Exception) -> str: """Return a human-readable multi-line description of an exception. Formats library-specific ``CalibratedError`` instances with structured details for diagnostics and logging. For other exceptions, returns the standard string representation. Parameters ---------- e : Exception The exception to format. Returns ------- str Multi-line human-readable message. For CalibratedError, includes class name, message, and details dict if present. Examples -------- >>> from calibrated_explanations.utils.exceptions import ValidationError, explain_exception >>> e = ValidationError("x must not be empty", details={"param": "x", "requirement": "non-empty"}) >>> print(explain_exception(e)) ValidationError: x must not be empty Details: {'param': 'x', 'requirement': 'non-empty'} """ if isinstance(e, CalibratedError): lines = [f"{e.__class__.__name__}: {str(e)}"] if e.details is not None: lines.append(f" Details: {e.details}") return "\n".join(lines) return str(e)
class PlotPluginError(CalibratedError): """Raised when a plot plugin encounters an unrecoverable error or is misconfigured.""" class MissingExtensionError(CalibratedError, ImportError): """Raised when an optional modality extension package is not installed (ADR-033)."""