or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-system.mdcommand-line-interface.mdconfiguration-management.mdcontent-entities.mdcore-download-api.mddownload-system.mdexception-handling.mdindex.mdplugin-system.mdtext-data-processing.md

exception-handling.mddocs/

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