| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- """Custom exception classes for the boilerplates CLI.
- This module defines specific exception types for better error handling
- and diagnostics throughout the application.
- """
- from __future__ import annotations
- from dataclasses import dataclass, field
- class BoilerplatesError(Exception):
- """Base exception for all boilerplates CLI errors."""
- pass
- class ConfigError(BoilerplatesError):
- """Raised when configuration operations fail."""
- pass
- class ConfigValidationError(ConfigError):
- """Raised when configuration validation fails."""
- pass
- class TemplateError(BoilerplatesError):
- """Base exception for template-related errors."""
- pass
- class TemplateNotFoundError(TemplateError):
- """Raised when a template cannot be found."""
- def __init__(self, template_id: str, module_name: str | None = None):
- self.template_id = template_id
- self.module_name = module_name
- msg = f"Template '{template_id}' not found"
- if module_name:
- msg += f" in module '{module_name}'"
- super().__init__(msg)
- class TemplateDraftError(TemplateError):
- """Raised when attempting to use a draft template."""
- def __init__(self, template_id: str, module_name: str | None = None):
- self.template_id = template_id
- self.module_name = module_name
- module_suffix = f" in module '{module_name}'" if module_name else ""
- msg = (
- f"Template '{template_id}' is in draft mode and not yet available for use{module_suffix}.\n"
- "Draft templates are work-in-progress and cannot be generated yet.\n"
- "To get updates when published, run 'boilerplates repo update' to sync your library."
- )
- super().__init__(msg)
- class DuplicateTemplateError(TemplateError):
- """Raised when duplicate template IDs are found within the same library."""
- def __init__(self, template_id: str, library_name: str):
- self.template_id = template_id
- self.library_name = library_name
- super().__init__(
- f"Duplicate template ID '{template_id}' found in library '{library_name}'. "
- f"Each template within a library must have a unique ID."
- )
- class TemplateLoadError(TemplateError):
- """Raised when a template fails to load."""
- pass
- class TemplateSyntaxError(TemplateError):
- """Raised when a Jinja2 template has syntax errors."""
- def __init__(self, template_id: str, errors: list[str]):
- self.template_id = template_id
- self.errors = errors
- msg = f"Jinja2 syntax errors in template '{template_id}':\n" + "\n".join(errors)
- super().__init__(msg)
- class TemplateValidationError(TemplateError):
- """Raised when template validation fails."""
- pass
- class IncompatibleSchemaVersionError(TemplateError):
- """Raised when a template uses a schema version not supported by the module."""
- def __init__(
- self,
- template_id: str,
- template_schema: str,
- module_schema: str,
- module_name: str,
- ):
- self.template_id = template_id
- self.template_schema = template_schema
- self.module_schema = module_schema
- self.module_name = module_name
- msg = (
- f"Template '{template_id}' uses schema version {template_schema}, "
- f"but module '{module_name}' only supports up to version {module_schema}.\n\n"
- f"This template requires features not available in your current CLI version.\n"
- f"Please upgrade the boilerplates CLI.\n\n"
- f"Run: pip install --upgrade boilerplates"
- )
- super().__init__(msg)
- @dataclass
- class RenderErrorContext:
- """Context information for template rendering errors."""
- file_path: str | None = None
- line_number: int | None = None
- column: int | None = None
- context_lines: list[str] = field(default_factory=list)
- variable_context: dict[str, str] = field(default_factory=dict)
- suggestions: list[str] = field(default_factory=list)
- original_error: Exception | None = None
- class TemplateRenderError(TemplateError):
- """Raised when template rendering fails."""
- def __init__(self, message: str, context: RenderErrorContext | None = None):
- self.context = context or RenderErrorContext()
- # Expose context fields as instance attributes for backward compatibility
- self.file_path = self.context.file_path
- self.line_number = self.context.line_number
- self.column = self.context.column
- self.context_lines = self.context.context_lines
- self.variable_context = self.context.variable_context
- self.suggestions = self.context.suggestions
- self.original_error = self.context.original_error
- # Build enhanced error message
- parts = [message]
- if self.context.file_path:
- location = f"File: {self.context.file_path}"
- if self.context.line_number:
- location += f", Line: {self.context.line_number}"
- if self.context.column:
- location += f", Column: {self.context.column}"
- parts.append(location)
- super().__init__("\n".join(parts))
- class VariableError(BoilerplatesError):
- """Base exception for variable-related errors."""
- pass
- class VariableValidationError(VariableError):
- """Raised when variable validation fails."""
- def __init__(self, variable_name: str, message: str):
- self.variable_name = variable_name
- msg = f"Validation error for variable '{variable_name}': {message}"
- super().__init__(msg)
- class VariableTypeError(VariableError):
- """Raised when a variable has an incorrect type."""
- def __init__(self, variable_name: str, expected_type: str, actual_type: str):
- self.variable_name = variable_name
- self.expected_type = expected_type
- self.actual_type = actual_type
- msg = f"Type error for variable '{variable_name}': expected {expected_type}, got {actual_type}"
- super().__init__(msg)
- class LibraryError(BoilerplatesError):
- """Raised when library operations fail."""
- pass
- class ModuleError(BoilerplatesError):
- """Raised when module operations fail."""
- pass
- class ModuleNotFoundError(ModuleError):
- """Raised when a module cannot be found."""
- def __init__(self, module_name: str):
- self.module_name = module_name
- msg = f"Module '{module_name}' not found"
- super().__init__(msg)
- class ModuleLoadError(ModuleError):
- """Raised when a module fails to load."""
- pass
- class SchemaError(BoilerplatesError):
- """Raised when schema operations fail."""
- def __init__(self, message: str, details: str | None = None):
- self.details = details
- msg = message
- if details:
- msg += f" ({details})"
- super().__init__(msg)
- class FileOperationError(BoilerplatesError):
- """Raised when file operations fail."""
- pass
- class RenderError(BoilerplatesError):
- """Raised when rendering operations fail."""
- pass
- class YAMLParseError(BoilerplatesError):
- """Raised when YAML parsing fails."""
- def __init__(self, file_path: str, original_error: Exception):
- self.file_path = file_path
- self.original_error = original_error
- msg = f"Failed to parse YAML file '{file_path}': {original_error}"
- super().__init__(msg)
|