mirror of
https://github.com/ChristianLempa/boilerplates.git
synced 2026-08-28 10:04:57 -05:00
197 lines
5.5 KiB
Python
197 lines
5.5 KiB
Python
"""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 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
|
|
|
|
|
|
@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 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)
|