"""Exception hierarchy for Confii.
This module defines a comprehensive exception hierarchy for better error
handling and debugging throughout the Confii library.
"""
from typing import Any, Dict, Optional
[docs]
class ConfiiError(Exception):
"""Base exception for all Confii errors.
All Confii specific exceptions inherit from this class,
making it easy to catch any Confii related error.
"""
[docs]
def __init__(self, message: str, context: Optional[Dict[str, Any]] = None) -> None:
"""Initialize the exception.
Args:
message: Human-readable error message
context: Optional context dictionary with additional error information
(e.g., file path, line number, key, value)
"""
super().__init__(message)
self.message = message
self.context = context or {}
[docs]
def __str__(self) -> str:
"""Return formatted error message with context if available."""
if self.context:
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
return f"{self.message} ({context_str})"
return self.message
[docs]
class ConfigLoadError(ConfiiError):
"""Raised when configuration loading fails.
This exception is raised when a loader cannot successfully load
a configuration from its source (file, remote URL, etc.).
"""
[docs]
def __init__(
self,
message: str,
source: Optional[str] = None,
loader_type: Optional[str] = None,
original_error: Optional[Exception] = None,
) -> None:
"""Initialize the configuration load error.
Args:
message: Error message
source: Source file/URL that failed to load
loader_type: Type of loader that failed
original_error: Original exception that caused this error
"""
context: Dict[str, Any] = {}
if source:
context["source"] = source
if loader_type:
context["loader_type"] = loader_type
if original_error:
context["original_error"] = str(original_error)
super().__init__(message, context)
self.source = source
self.loader_type = loader_type
self.original_error = original_error
[docs]
class ConfigValidationError(ConfiiError):
"""Raised when configuration validation fails.
This exception is raised when configuration values don't match
the expected schema or validation rules.
"""
[docs]
def __init__(
self,
message: str,
key: Optional[str] = None,
value: Any = None,
schema_path: Optional[str] = None,
validation_errors: Optional[list] = None,
original_error: Optional[Exception] = None,
) -> None:
"""Initialize the configuration validation error.
Args:
message: Error message
key: Configuration key that failed validation
value: Value that failed validation
schema_path: Path to schema definition (if applicable)
validation_errors: List of detailed validation errors
original_error: Original exception that caused this error
"""
context: Dict[str, Any] = {}
if key:
context["key"] = key
if value is not None:
context["value"] = value
if schema_path:
context["schema_path"] = schema_path
if validation_errors:
context["validation_errors"] = validation_errors
if original_error:
context["original_error"] = str(original_error)
super().__init__(message, context)
self.key = key
self.value = value
self.schema_path = schema_path
self.validation_errors = validation_errors or []
self.original_error = original_error
[docs]
class ConfigMergeConflictError(ConfiiError):
"""Raised when configuration merge conflicts occur.
This exception is raised when merging configurations results in
unresolvable conflicts (e.g., incompatible types, conflicting values).
"""
[docs]
def __init__(
self,
message: str,
key: str,
old_value: Any = None,
new_value: Any = None,
old_source: Optional[str] = None,
new_source: Optional[str] = None,
) -> None:
"""Initialize the configuration merge conflict error.
Args:
message: Error message
key: Configuration key that has a conflict
old_value: Value from the base/old configuration
new_value: Value from the new configuration
old_source: Source of the old value
new_source: Source of the new value
"""
context: Dict[str, Any] = {"key": key}
if old_value is not None:
context["old_value"] = old_value
if new_value is not None:
context["new_value"] = new_value
if old_source:
context["old_source"] = old_source
if new_source:
context["new_source"] = new_source
super().__init__(message, context)
self.key = key
self.old_value = old_value
self.new_value = new_value
self.old_source = old_source
self.new_source = new_source
[docs]
class ConfigNotFoundError(ConfiiError):
"""Raised when a configuration key or value is not found.
This exception is raised when trying to access a configuration
key that doesn't exist.
"""
[docs]
def __init__(
self, message: str, key: str, available_keys: Optional[list] = None
) -> None:
"""Initialize the configuration not found error.
Args:
message: Error message
key: Configuration key that was not found
available_keys: List of available keys (for helpful error messages)
"""
context: Dict[str, Any] = {"key": key}
if available_keys:
context["available_keys"] = available_keys
super().__init__(message, context)
self.key = key
self.available_keys = available_keys
[docs]
class ConfigAccessError(ConfiiError):
"""Raised when accessing configuration fails.
This exception is raised when configuration cannot be accessed
due to permission issues, network problems, etc.
"""
[docs]
def __init__(
self,
message: str,
source: Optional[str] = None,
operation: Optional[str] = None,
original_error: Optional[Exception] = None,
) -> None:
"""Initialize the configuration access error.
Args:
message: Error message
source: Source that cannot be accessed
operation: Operation that failed (read, write, list, etc.)
original_error: Original exception that caused this error
"""
context: Dict[str, Any] = {}
if source:
context["source"] = source
if operation:
context["operation"] = operation
if original_error:
context["original_error"] = str(original_error)
super().__init__(message, context)
self.source = source
self.operation = operation
self.original_error = original_error