OPEN SOURCE
software/src/exceptions.py
Read on this site and download the files you need.
Nichijoyaku was created using AI. A complete review by human biblical-language and biblical-studies specialists has not been established. Read alongside established translations and original-language resources.
← Back to the open-source guide
1"""Custom exception hierarchy for the Bible translator.23Provides specific exception types for different error scenarios to enable4proper error handling and recovery strategies.5"""67from __future__ import annotations8910class TranslationError(Exception):11"""Base exception for all translation-related errors."""1213def __init__(self, message: str, details: dict | None = None) -> None:14"""Initialize the translation error.1516Args:17message: Human-readable error message.18details: Optional dictionary with additional error details.19"""20super().__init__(message)21self.details = details or {}222324class ClaudeAPIError(TranslationError):25"""Error communicating with Claude CLI/API."""2627def __init__(28self,29message: str,30stderr: str | None = None,31return_code: int | None = None,32) -> None:33"""Initialize the Claude API error.3435Args:36message: Human-readable error message.37stderr: Standard error output from the CLI.38return_code: Process return code.39"""40details = {41"stderr": stderr,42"return_code": return_code,43}44super().__init__(message, details)45self.stderr = stderr46self.return_code = return_code474849class RateLimitError(ClaudeAPIError):50"""Rate limit exceeded when calling Claude."""5152def __init__(53self,54message: str = "Rate limit exceeded",55retry_after: float | None = None,56) -> None:57"""Initialize the rate limit error.5859Args:60message: Human-readable error message.61retry_after: Suggested wait time in seconds before retrying.62"""63super().__init__(message)64self.retry_after = retry_after65self.details["retry_after"] = retry_after666768class ModelNotFoundError(ClaudeAPIError):69"""Requested model is not available."""7071def __init__(self, model: str) -> None:72"""Initialize the model not found error.7374Args:75model: The model identifier that was not found.76"""77super().__init__(f"Model not found: {model}")78self.model = model79self.details["model"] = model808182class CLINotFoundError(ClaudeAPIError):83"""Claude CLI is not installed or not in PATH."""8485def __init__(self) -> None:86"""Initialize the CLI not found error."""87super().__init__(88"Claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code"89)909192class VerseNotFoundError(TranslationError):93"""Requested verse does not exist."""9495def __init__(self, book: str, chapter: int, verse: int) -> None:96"""Initialize the verse not found error.9798Args:99book: Book name.100chapter: Chapter number.101verse: Verse number.102"""103super().__init__(f"Verse not found: {book} {chapter}:{verse}")104self.book = book105self.chapter = chapter106self.verse = verse107self.details = {"book": book, "chapter": chapter, "verse": verse}108109110class InvalidReferenceError(TranslationError):111"""Invalid verse reference format."""112113def __init__(self, reference: str, reason: str | None = None) -> None:114"""Initialize the invalid reference error.115116Args:117reference: The malformed reference string.118reason: Optional explanation of what's wrong.119"""120msg = f"Invalid reference format: {reference}"121if reason:122msg += f" ({reason})"123super().__init__(msg)124self.reference = reference125self.reason = reason126self.details = {"reference": reference, "reason": reason}127128129class ChunkParsingError(TranslationError):130"""Error parsing chunk/pericope translation response."""131132def __init__(133self,134message: str,135response: str | None = None,136expected_verses: list[str] | None = None,137) -> None:138"""Initialize the chunk parsing error.139140Args:141message: Human-readable error message.142response: The raw response that failed to parse.143expected_verses: List of verse references that were expected.144"""145super().__init__(message)146self.response = response147self.expected_verses = expected_verses148self.details = {149"response_preview": response[:500] if response else None,150"expected_verses": expected_verses,151}152153154class PericopeNotFoundError(TranslationError):155"""Requested pericope does not exist."""156157def __init__(self, pericope_id: str) -> None:158"""Initialize the pericope not found error.159160Args:161pericope_id: The pericope identifier that was not found.162"""163super().__init__(f"Pericope not found: {pericope_id}")164self.pericope_id = pericope_id165self.details = {"pericope_id": pericope_id}166167168class EvaluationError(TranslationError):169"""Error during translation evaluation."""170171def __init__(172self,173message: str,174verse_ref: str | None = None,175dimension: str | None = None,176) -> None:177"""Initialize the evaluation error.178179Args:180message: Human-readable error message.181verse_ref: The verse being evaluated.182dimension: The evaluation dimension that failed.183"""184super().__init__(message)185self.verse_ref = verse_ref186self.dimension = dimension187self.details = {"verse_ref": verse_ref, "dimension": dimension}188189190class QualityThresholdError(TranslationError):191"""Translation failed to meet quality threshold."""192193def __init__(194self,195message: str,196score: float,197threshold: float,198dimension: str | None = None,199) -> None:200"""Initialize the quality threshold error.201202Args:203message: Human-readable error message.204score: The actual score achieved.205threshold: The required minimum score.206dimension: The dimension that failed (if specific).207"""208super().__init__(message)209self.score = score210self.threshold = threshold211self.dimension = dimension212self.details = {213"score": score,214"threshold": threshold,215"dimension": dimension,216}217218219class StorageError(TranslationError):220"""Error reading or writing translation data."""221222def __init__(223self,224message: str,225path: str | None = None,226operation: str | None = None,227) -> None:228"""Initialize the storage error.229230Args:231message: Human-readable error message.232path: The file path involved.233operation: The operation that failed (read/write).234"""235super().__init__(message)236self.path = path237self.operation = operation238self.details = {"path": path, "operation": operation}239240241class GlossaryMatchError(TranslationError):242"""Error in glossary term matching."""243244def __init__(self, message: str, term: str | None = None) -> None:245"""Initialize the glossary match error.246247Args:248message: Human-readable error message.249term: The term being matched.250"""251super().__init__(message)252self.term = term253self.details = {"term": term}254255256class ConfigurationError(TranslationError):257"""Error in configuration loading or validation."""258259def __init__(self, message: str, config_key: str | None = None) -> None:260"""Initialize the configuration error.261262Args:263message: Human-readable error message.264config_key: The configuration key that caused the error.265"""266super().__init__(message)267self.config_key = config_key268self.details = {"config_key": config_key}269270271def classify_claude_error(stderr: str, return_code: int) -> ClaudeAPIError:272"""Classify a Claude CLI error based on stderr and return code.273274Args:275stderr: Standard error output from the CLI.276return_code: Process return code.277278Returns:279Appropriate ClaudeAPIError subclass instance.280"""281stderr_lower = stderr.lower()282283# Check for rate limiting284if "rate limit" in stderr_lower or "429" in stderr_lower:285return RateLimitError(stderr)286287# Check for model not found288if "model" in stderr_lower and ("not found" in stderr_lower or "invalid" in stderr_lower):289# Try to extract model name290return ClaudeAPIError(f"Model error: {stderr}", stderr, return_code)291292# Check for CLI not installed293if return_code == 127 or "command not found" in stderr_lower:294return CLINotFoundError()295296# Check for network errors297if "network" in stderr_lower or "connection" in stderr_lower or "timeout" in stderr_lower:298return ClaudeAPIError(f"Network error: {stderr}", stderr, return_code)299300# Default to generic API error301return ClaudeAPIError(f"Claude CLI failed: {stderr}", stderr, return_code)302