0
# Exception Handling
1
2
Comprehensive exception hierarchy with context support for robust error handling and debugging. Provides specific error types for different failure scenarios with detailed context information and recovery suggestions.
3
4
## Types
5
6
```python { .api }
7
from typing import Dict, Any, List, Optional, Union
8
```
9
10
## Capabilities
11
12
### Base Exception Class
13
14
Foundation exception class with context support and enhanced error information.
15
16
```python { .api }
17
class JmcomicException(Exception):
18
"""
19
Base exception class with context support and enhanced error information.
20
21
All JMComic exceptions inherit from this base class, providing
22
consistent error handling with context information and debugging support.
23
24
Attributes:
25
- message: str - Error message
26
- context: Dict[str, Any] - Additional context information
27
- inner_exception: Exception - Original exception if wrapped
28
- error_code: str - Unique error code for categorization
29
30
Methods:
31
- add_context(key, value): Add context information
32
- get_context(key, default=None): Get context value
33
- has_context(key): Check if context key exists
34
- get_full_message(): Get message with context
35
- to_dict(): Convert exception to dictionary
36
"""
37
38
def __init__(self, message: str, context: Dict[str, Any] = None,
39
inner_exception: Exception = None):
40
"""
41
Initialize exception with message and optional context.
42
43
Parameters:
44
- message: str - Error message
45
- context: dict, optional - Additional context information
46
- inner_exception: Exception, optional - Original exception
47
"""
48
super().__init__(message)
49
self.message = message
50
self.context = context or {}
51
self.inner_exception = inner_exception
52
self.error_code = self.__class__.__name__
53
54
def add_context(self, key: str, value: Any):
55
"""
56
Add context information to the exception.
57
58
Parameters:
59
- key: str - Context key
60
- value: Any - Context value
61
"""
62
self.context[key] = value
63
64
def get_context(self, key: str, default: Any = None) -> Any:
65
"""
66
Get context value by key.
67
68
Parameters:
69
- key: str - Context key
70
- default: Any - Default value if key not found
71
72
Returns:
73
Any - Context value or default
74
"""
75
return self.context.get(key, default)
76
77
def get_full_message(self) -> str:
78
"""
79
Get complete error message including context.
80
81
Returns:
82
str - Full error message with context information
83
"""
84
if not self.context:
85
return self.message
86
87
context_str = ", ".join(f"{k}={v}" for k, v in self.context.items())
88
return f"{self.message} (Context: {context_str})"
89
90
def to_dict(self) -> Dict[str, Any]:
91
"""
92
Convert exception to dictionary representation.
93
94
Returns:
95
dict - Exception data as dictionary
96
"""
97
return {
98
'error_code': self.error_code,
99
'message': self.message,
100
'context': self.context,
101
'inner_exception': str(self.inner_exception) if self.inner_exception else None
102
}
103
```
104
105
### HTTP and Network Exceptions
106
107
Exceptions related to HTTP requests, responses, and network communication.
108
109
```python { .api }
110
class ResponseUnexpectedException(JmcomicException):
111
"""
112
Exception for HTTP response errors and unexpected status codes.
113
114
Raised when HTTP requests return unexpected status codes or
115
response content that doesn't match expected format.
116
117
Attributes:
118
- status_code: int - HTTP status code
119
- response_url: str - URL that caused the error
120
- response_headers: Dict[str, str] - Response headers
121
- response_content: str - Response content (truncated)
122
"""
123
124
def __init__(self, message: str, status_code: int = None,
125
response_url: str = None, response_content: str = None):
126
"""
127
Initialize HTTP response exception.
128
129
Parameters:
130
- message: str - Error message
131
- status_code: int, optional - HTTP status code
132
- response_url: str, optional - URL that failed
133
- response_content: str, optional - Response content
134
"""
135
context = {}
136
if status_code is not None:
137
context['status_code'] = status_code
138
if response_url:
139
context['url'] = response_url
140
if response_content:
141
context['content_preview'] = response_content[:500]
142
143
super().__init__(message, context)
144
self.status_code = status_code
145
self.response_url = response_url
146
self.response_content = response_content
147
148
class RequestRetryAllFailException(JmcomicException):
149
"""
150
Exception when all retry attempts have been exhausted.
151
152
Raised when multiple retry attempts fail and no more retries
153
are available according to the configuration.
154
155
Attributes:
156
- retry_count: int - Number of retry attempts made
157
- last_exception: Exception - Last exception encountered
158
- failed_attempts: List[Exception] - All failed attempts
159
"""
160
161
def __init__(self, message: str, retry_count: int,
162
failed_attempts: List[Exception]):
163
"""
164
Initialize retry exhaustion exception.
165
166
Parameters:
167
- message: str - Error message
168
- retry_count: int - Number of retries attempted
169
- failed_attempts: List[Exception] - All failed attempts
170
"""
171
context = {
172
'retry_count': retry_count,
173
'attempt_count': len(failed_attempts)
174
}
175
super().__init__(message, context)
176
self.retry_count = retry_count
177
self.failed_attempts = failed_attempts
178
self.last_exception = failed_attempts[-1] if failed_attempts else None
179
```
180
181
### Data Processing Exceptions
182
183
Exceptions related to data parsing, validation, and processing operations.
184
185
```python { .api }
186
class RegularNotMatchException(JmcomicException):
187
"""
188
Exception for HTML parsing and regex matching failures.
189
190
Raised when regular expressions fail to match expected patterns
191
in HTML content or when required data cannot be extracted.
192
193
Attributes:
194
- pattern: str - Regex pattern that failed to match
195
- content_sample: str - Sample of content that failed to match
196
"""
197
198
def __init__(self, message: str, pattern: str = None, content: str = None):
199
"""
200
Initialize regex matching exception.
201
202
Parameters:
203
- message: str - Error message
204
- pattern: str, optional - Regex pattern that failed
205
- content: str, optional - Content that failed to match
206
"""
207
context = {}
208
if pattern:
209
context['pattern'] = pattern
210
if content:
211
context['content_sample'] = content[:200]
212
213
super().__init__(message, context)
214
self.pattern = pattern
215
self.content_sample = content
216
217
class JsonResolveFailException(JmcomicException):
218
"""
219
Exception for JSON parsing and validation errors.
220
221
Raised when JSON content cannot be parsed or doesn't match
222
the expected structure for API responses.
223
224
Attributes:
225
- json_content: str - JSON content that failed to parse
226
- parse_error: str - Specific parsing error message
227
"""
228
229
def __init__(self, message: str, json_content: str = None,
230
parse_error: str = None):
231
"""
232
Initialize JSON parsing exception.
233
234
Parameters:
235
- message: str - Error message
236
- json_content: str, optional - JSON that failed to parse
237
- parse_error: str, optional - Specific parsing error
238
"""
239
context = {}
240
if json_content:
241
context['json_preview'] = json_content[:300]
242
if parse_error:
243
context['parse_error'] = parse_error
244
245
super().__init__(message, context)
246
self.json_content = json_content
247
self.parse_error = parse_error
248
```
249
250
### Content Availability Exceptions
251
252
Exceptions related to missing or unavailable content.
253
254
```python { .api }
255
class MissingAlbumPhotoException(JmcomicException):
256
"""
257
Exception for content not found or unavailable errors.
258
259
Raised when requested albums, photos, or images cannot be found
260
or are no longer available on the platform.
261
262
Attributes:
263
- content_type: str - Type of missing content ('album', 'photo', 'image')
264
- content_id: str - ID of the missing content
265
- availability_status: str - Reason for unavailability
266
"""
267
268
def __init__(self, message: str, content_type: str = None,
269
content_id: str = None, availability_status: str = None):
270
"""
271
Initialize missing content exception.
272
273
Parameters:
274
- message: str - Error message
275
- content_type: str, optional - Type of missing content
276
- content_id: str, optional - ID of missing content
277
- availability_status: str, optional - Reason for unavailability
278
"""
279
context = {}
280
if content_type:
281
context['content_type'] = content_type
282
if content_id:
283
context['content_id'] = content_id
284
if availability_status:
285
context['availability_status'] = availability_status
286
287
super().__init__(message, context)
288
self.content_type = content_type
289
self.content_id = content_id
290
self.availability_status = availability_status
291
```
292
293
### Download Operation Exceptions
294
295
Exceptions specific to download operations and partial failures.
296
297
```python { .api }
298
class PartialDownloadFailedException(JmcomicException):
299
"""
300
Exception for partial download failures.
301
302
Raised when some parts of a download operation fail while others
303
succeed. Contains information about successful and failed operations.
304
305
Attributes:
306
- successful_downloads: List[str] - IDs of successful downloads
307
- failed_downloads: List[str] - IDs of failed downloads
308
- failure_details: Dict[str, Exception] - Detailed failure information
309
- success_rate: float - Percentage of successful downloads
310
"""
311
312
def __init__(self, message: str, successful_downloads: List[str] = None,
313
failed_downloads: List[str] = None,
314
failure_details: Dict[str, Exception] = None):
315
"""
316
Initialize partial download failure exception.
317
318
Parameters:
319
- message: str - Error message
320
- successful_downloads: list, optional - Successful download IDs
321
- failed_downloads: list, optional - Failed download IDs
322
- failure_details: dict, optional - Detailed failure information
323
"""
324
successful_downloads = successful_downloads or []
325
failed_downloads = failed_downloads or []
326
total = len(successful_downloads) + len(failed_downloads)
327
success_rate = (len(successful_downloads) / total * 100) if total > 0 else 0
328
329
context = {
330
'successful_count': len(successful_downloads),
331
'failed_count': len(failed_downloads),
332
'success_rate': f"{success_rate:.1f}%"
333
}
334
335
super().__init__(message, context)
336
self.successful_downloads = successful_downloads
337
self.failed_downloads = failed_downloads
338
self.failure_details = failure_details or {}
339
self.success_rate = success_rate
340
```
341
342
### Exception Utilities
343
344
Utility class for exception creation, context management, and error handling.
345
346
```python { .api }
347
class ExceptionTool:
348
"""
349
Exception creation and context management utilities.
350
351
Provides helper functions for creating exceptions with context,
352
validating conditions, and managing error scenarios.
353
354
Static Methods:
355
- require_true(condition, message, exception_class=None): Assert condition
356
- require_not_none(value, message): Assert value is not None
357
- require_not_empty(collection, message): Assert collection not empty
358
- wrap_exception(exception, message, context=None): Wrap existing exception
359
- create_with_context(exception_class, message, **context): Create with context
360
"""
361
362
@staticmethod
363
def require_true(condition: bool, message: str,
364
exception_class: type = None) -> None:
365
"""
366
Assert that condition is true, raise exception if false.
367
368
Parameters:
369
- condition: bool - Condition to check
370
- message: str - Error message if condition fails
371
- exception_class: type, optional - Exception class to raise
372
373
Raises:
374
JmcomicException or specified exception - If condition is false
375
"""
376
if not condition:
377
exc_class = exception_class or JmcomicException
378
raise exc_class(message)
379
380
@staticmethod
381
def require_not_none(value: Any, message: str) -> None:
382
"""
383
Assert that value is not None.
384
385
Parameters:
386
- value: Any - Value to check
387
- message: str - Error message if None
388
389
Raises:
390
JmcomicException - If value is None
391
"""
392
ExceptionTool.require_true(value is not None, message)
393
394
@staticmethod
395
def require_not_empty(collection: Union[List, Dict, str], message: str) -> None:
396
"""
397
Assert that collection is not empty.
398
399
Parameters:
400
- collection: list/dict/str - Collection to check
401
- message: str - Error message if empty
402
403
Raises:
404
JmcomicException - If collection is empty
405
"""
406
ExceptionTool.require_true(len(collection) > 0, message)
407
408
@staticmethod
409
def wrap_exception(exception: Exception, message: str,
410
context: Dict[str, Any] = None) -> JmcomicException:
411
"""
412
Wrap existing exception with additional context.
413
414
Parameters:
415
- exception: Exception - Original exception
416
- message: str - New error message
417
- context: dict, optional - Additional context
418
419
Returns:
420
JmcomicException - Wrapped exception with context
421
"""
422
wrapped = JmcomicException(message, context, exception)
423
wrapped.add_context('original_exception_type', type(exception).__name__)
424
return wrapped
425
426
@staticmethod
427
def create_with_context(exception_class: type, message: str,
428
**context) -> JmcomicException:
429
"""
430
Create exception with context from keyword arguments.
431
432
Parameters:
433
- exception_class: type - Exception class to create
434
- message: str - Error message
435
- **context: Additional context as keyword arguments
436
437
Returns:
438
JmcomicException - Created exception with context
439
"""
440
return exception_class(message, context)
441
```
442
443
## Usage Examples
444
445
```python
446
# Basic exception handling
447
try:
448
album = download_album("invalid_id")
449
except JmcomicException as e:
450
print(f"Error: {e.get_full_message()}")
451
print(f"Error code: {e.error_code}")
452
453
# Access specific context
454
if e.has_context('album_id'):
455
print(f"Failed album ID: {e.get_context('album_id')}")
456
457
# HTTP response error handling
458
try:
459
response = client.get_album_detail("123456")
460
except ResponseUnexpectedException as e:
461
print(f"HTTP Error: {e.status_code}")
462
print(f"URL: {e.response_url}")
463
print(f"Content preview: {e.get_context('content_preview')}")
464
465
# Partial download failure handling
466
try:
467
results = download_batch(download_album, album_ids)
468
except PartialDownloadFailedException as e:
469
print(f"Success rate: {e.success_rate}%")
470
print(f"Successful: {len(e.successful_downloads)}")
471
print(f"Failed: {len(e.failed_downloads)}")
472
473
# Process partial results
474
for album_id in e.successful_downloads:
475
print(f"Successfully downloaded: {album_id}")
476
477
for album_id, error in e.failure_details.items():
478
print(f"Failed to download {album_id}: {error}")
479
480
# Using ExceptionTool for validation
481
try:
482
ExceptionTool.require_not_none(album_id, "Album ID cannot be None")
483
ExceptionTool.require_not_empty(photo_list, "Photo list cannot be empty")
484
ExceptionTool.require_true(len(album_id) > 0, "Album ID must not be empty")
485
except JmcomicException as e:
486
print(f"Validation error: {e.message}")
487
488
# Creating exceptions with context
489
error = ExceptionTool.create_with_context(
490
MissingAlbumPhotoException,
491
"Album not found",
492
album_id="123456",
493
content_type="album",
494
availability_status="removed"
495
)
496
raise error
497
```
498
499
## Exception Hierarchy
500
501
The exception hierarchy provides specific error types for different scenarios:
502
503
- **JmcomicException**: Base class with context support
504
- **ResponseUnexpectedException**: HTTP and network errors
505
- **RequestRetryAllFailException**: Retry exhaustion
506
- **RegularNotMatchException**: HTML parsing failures
507
- **JsonResolveFailException**: JSON parsing errors
508
- **MissingAlbumPhotoException**: Content not found
509
- **PartialDownloadFailedException**: Partial operation failures
510
511
## Error Recovery Strategies
512
513
The exception system supports various recovery strategies:
514
515
1. **Retry Logic**: Use `RequestRetryAllFailException` to implement retry mechanisms
516
2. **Partial Processing**: Handle `PartialDownloadFailedException` to process successful parts
517
3. **Graceful Degradation**: Use context information to provide alternative approaches
518
4. **User Feedback**: Rich error messages and context for user-friendly error reporting