Coverage for src / minibook / exceptions.py: 100%
56 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-27 10:03 +0000
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-27 10:03 +0000
1"""Custom exceptions for MiniBook.
3This module defines a hierarchy of exceptions for better error handling
4and more informative error messages throughout the MiniBook codebase.
5"""
8class MinibookError(Exception):
9 """Base exception for all MiniBook errors.
11 All MiniBook-specific exceptions inherit from this class,
12 allowing callers to catch all MiniBook errors with a single except clause.
14 Examples:
15 >>> try:
16 ... raise MinibookError("Something went wrong")
17 ... except MinibookError as e:
18 ... print(f"MiniBook error: {e}")
19 MiniBook error: Something went wrong
20 """
23class ValidationError(MinibookError):
24 """Exception raised when input validation fails.
26 This exception is raised when user input doesn't meet
27 the required format or constraints.
29 Attributes:
30 field: The name of the field that failed validation
31 value: The invalid value that was provided
32 message: Description of what went wrong
34 Examples:
35 >>> raise ValidationError("url", "javascript:alert(1)", "Invalid URL scheme")
36 Traceback (most recent call last):
37 ...
38 minibook.exceptions.ValidationError: Invalid URL scheme (field: url, value: javascript:alert(1))
39 """
41 def __init__(self, field: str, value: str | None = None, message: str = "Validation failed"):
42 """Initialize validation error.
44 Args:
45 field: The name of the field that failed validation.
46 value: The invalid value that was provided.
47 message: Description of what went wrong.
48 """
49 self.field = field
50 self.value = value
51 self.message = message
53 if value is not None:
54 super().__init__(f"{message} (field: {field}, value: {value})")
55 else:
56 super().__init__(f"{message} (field: {field})")
59class URLValidationError(ValidationError):
60 """Exception raised when URL validation fails.
62 A specialized validation error for URL-specific issues.
64 Examples:
65 >>> raise URLValidationError("javascript:alert(1)", "Dangerous URL scheme")
66 Traceback (most recent call last):
67 ...
68 minibook.exceptions.URLValidationError: Dangerous URL scheme (field: url, value: javascript:alert(1))
69 """
71 def __init__(self, url: str, message: str = "Invalid URL"):
72 """Initialize URL validation error.
74 Args:
75 url: The invalid URL.
76 message: Description of what went wrong.
77 """
78 super().__init__(field="url", value=url, message=message)
79 self.url = url
82class LinkNameValidationError(ValidationError):
83 """Exception raised when link name validation fails.
85 Examples:
86 >>> raise LinkNameValidationError("", "Link name cannot be empty")
87 Traceback (most recent call last):
88 ...
89 minibook.exceptions.LinkNameValidationError: Link name cannot be empty (field: name, value: )
90 """
92 def __init__(self, name: str, message: str = "Invalid link name"):
93 """Initialize link name validation error.
95 Args:
96 name: The invalid link name.
97 message: Description of what went wrong.
98 """
99 super().__init__(field="name", value=name, message=message)
100 self.name = name
103class TemplateError(MinibookError):
104 """Exception raised when template operations fail.
106 This exception covers template loading, parsing, and rendering errors.
108 Attributes:
109 template_path: Path to the template that caused the error
110 message: Description of what went wrong
112 Examples:
113 >>> raise TemplateError("/path/to/missing.j2", "Template file not found")
114 Traceback (most recent call last):
115 ...
116 minibook.exceptions.TemplateError: Template file not found: /path/to/missing.j2
117 """
119 def __init__(self, template_path: str | None = None, message: str = "Template error"):
120 """Initialize template error.
122 Args:
123 template_path: Path to the template that caused the error.
124 message: Description of what went wrong.
125 """
126 self.template_path = template_path
128 if template_path:
129 super().__init__(f"{message}: {template_path}")
130 else:
131 super().__init__(message)
134class TemplateNotFoundError(TemplateError):
135 """Exception raised when a template file cannot be found.
137 Examples:
138 >>> raise TemplateNotFoundError("/path/to/missing.j2")
139 Traceback (most recent call last):
140 ...
141 minibook.exceptions.TemplateNotFoundError: Template file not found: /path/to/missing.j2
142 """
144 def __init__(self, template_path: str):
145 """Initialize template not found error.
147 Args:
148 template_path: Path to the template that was not found.
149 """
150 super().__init__(template_path, "Template file not found")
153class PluginError(MinibookError):
154 """Exception raised when plugin operations fail.
156 This exception covers plugin loading, registration, and execution errors.
158 Attributes:
159 plugin_name: Name of the plugin that caused the error
160 message: Description of what went wrong
162 Examples:
163 >>> raise PluginError("pdf", "Required dependency fpdf2 not installed")
164 Traceback (most recent call last):
165 ...
166 minibook.exceptions.PluginError: Plugin 'pdf' error: Required dependency fpdf2 not installed
167 """
169 def __init__(self, plugin_name: str | None = None, message: str = "Plugin error"):
170 """Initialize plugin error.
172 Args:
173 plugin_name: Name of the plugin that caused the error.
174 message: Description of what went wrong.
175 """
176 self.plugin_name = plugin_name
178 if plugin_name:
179 super().__init__(f"Plugin '{plugin_name}' error: {message}")
180 else:
181 super().__init__(message)
184class PluginNotFoundError(PluginError):
185 """Exception raised when a requested plugin is not found.
187 Examples:
188 >>> raise PluginNotFoundError("unknown_format")
189 Traceback (most recent call last):
190 ...
191 minibook.exceptions.PluginNotFoundError: Plugin 'unknown_format' error: Output format not found
192 """
194 def __init__(self, plugin_name: str):
195 """Initialize plugin not found error.
197 Args:
198 plugin_name: Name of the plugin that was not found.
199 """
200 super().__init__(plugin_name, "Output format not found")
203class PluginDependencyError(PluginError):
204 """Exception raised when a plugin's dependencies are not installed.
206 Attributes:
207 dependency: The missing dependency name
208 install_command: Command to install the dependency
210 Examples:
211 >>> raise PluginDependencyError("", "fpdf2", "pip install minibook[pdf]")
212 Traceback (most recent call last):
213 ...
214 minibook.exceptions.PluginDependencyError: Missing dependency 'fpdf2'. Install with: pip install minibook[pdf]
215 """
217 def __init__(self, plugin_name: str, dependency: str, install_command: str | None = None):
218 """Initialize plugin dependency error.
220 Args:
221 plugin_name: Name of the plugin that caused the error.
222 dependency: The missing dependency name.
223 install_command: Command to install the dependency.
224 """
225 self.dependency = dependency
226 self.install_command = install_command
228 message = f"Missing dependency '{dependency}'"
229 if install_command:
230 message += f". Install with: {install_command}"
232 super().__init__(plugin_name, message)
235class ParseError(MinibookError):
236 """Exception raised when parsing input fails.
238 This exception is raised when JSON or other input formats cannot be parsed.
240 Attributes:
241 input_type: The type of input being parsed (e.g., "JSON", "YAML")
242 message: Description of what went wrong
244 Examples:
245 >>> raise ParseError("JSON", "Invalid JSON syntax")
246 Traceback (most recent call last):
247 ...
248 minibook.exceptions.ParseError: Failed to parse JSON: Invalid JSON syntax
249 """
251 def __init__(self, input_type: str = "input", message: str = "Parse error"):
252 """Initialize parse error.
254 Args:
255 input_type: The type of input being parsed (e.g., "JSON", "YAML").
256 message: Description of what went wrong.
257 """
258 self.input_type = input_type
259 super().__init__(f"Failed to parse {input_type}: {message}")
262class JSONParseError(ParseError):
263 """Exception raised when JSON parsing fails.
265 Examples:
266 >>> raise JSONParseError("Unexpected token at position 5")
267 Traceback (most recent call last):
268 ...
269 minibook.exceptions.JSONParseError: Failed to parse JSON: Unexpected token at position 5
270 """
272 def __init__(self, message: str = "Invalid JSON"):
273 """Initialize JSON parse error.
275 Args:
276 message: Description of what went wrong.
277 """
278 super().__init__("JSON", message)
281class OutputError(MinibookError):
282 """Exception raised when output generation fails.
284 Attributes:
285 output_path: Path where output was being written
286 message: Description of what went wrong
288 Examples:
289 >>> raise OutputError("/output/file.html", "Permission denied")
290 Traceback (most recent call last):
291 ...
292 minibook.exceptions.OutputError: Failed to write output to /output/file.html: Permission denied
293 """
295 def __init__(self, output_path: str | None = None, message: str = "Output error"):
296 """Initialize output error.
298 Args:
299 output_path: Path where output was being written.
300 message: Description of what went wrong.
301 """
302 self.output_path = output_path
304 if output_path:
305 super().__init__(f"Failed to write output to {output_path}: {message}")
306 else:
307 super().__init__(message)