"""Custom exception classes for the PAT Manager system.""" from __future__ import annotations class PatManagerError(Exception): """Base exception for PAT Manager errors.""" pass class RegistryValidationError(PatManagerError): """Raised when a registry entry fails validation. Attributes: entry: The raw dictionary that failed validation. failed_fields: List of field names that failed validation checks. """ def __init__(self, entry: dict, failed_fields: list[str]) -> None: self.entry = entry self.failed_fields = failed_fields fields_str = ", ".join(failed_fields) super().__init__( f"Registry entry validation failed for fields: {fields_str}. " f"Entry: {entry}" ) class ServiceCheckError(PatManagerError): """Raised when a service API check fails after retries. Attributes: service: The service name that failed (e.g. "GitLab"). token_name: The token identifier that was being checked. reason: Human-readable description of the failure. """ def __init__(self, service: str, token_name: str, reason: str) -> None: self.service = service self.token_name = token_name self.reason = reason super().__init__( f"Service check failed for {service}/{token_name}: {reason}" ) class RotationError(PatManagerError): """Raised when GitLab token rotation fails. Attributes: token_name: The token identifier that failed rotation. http_status: The HTTP status code returned, or None if no response. reason: Human-readable description of the failure. """ def __init__( self, token_name: str, http_status: int | None, reason: str ) -> None: self.token_name = token_name self.http_status = http_status self.reason = reason status_info = f" (HTTP {http_status})" if http_status else "" super().__init__( f"Token rotation failed for {token_name}{status_info}: {reason}" ) class SecretUpdateError(PatManagerError): """Raised when GitHub secret update fails. Attributes: secret_name: The name of the secret that could not be updated. reason: Human-readable description of the failure. """ def __init__(self, secret_name: str, reason: str) -> None: self.secret_name = secret_name self.reason = reason super().__init__( f"Failed to update GitHub secret '{secret_name}': {reason}" ) class AlertError(PatManagerError): """Raised when alert delivery fails. Attributes: channel: The alert channel that failed (e.g. "orgmylife", "email"). reason: Human-readable description of the failure. """ def __init__(self, channel: str, reason: str) -> None: self.channel = channel self.reason = reason super().__init__( f"Alert delivery failed via {channel}: {reason}" )