0
# Exception System
1
2
Comprehensive exception hierarchy for handling validation errors, conversion failures, and data processing issues in Schematics. The exception system provides detailed error information with field-level granularity and supports error aggregation for complex data structures.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Foundation exception classes providing common error handling functionality.
9
10
```python { .api }
11
class BaseError(Exception):
12
"""
13
Base class for all Schematics errors with error aggregation.
14
15
Provides immutable error storage and primitive representation
16
for machine-readable error processing.
17
"""
18
19
def __init__(self, errors):
20
"""
21
Initialize base error.
22
23
Args:
24
errors: Error data (dict/list/string)
25
"""
26
27
@property
28
def errors(self):
29
"""
30
Get immutable error data.
31
32
Returns:
33
Frozen error structure
34
"""
35
36
def to_primitive(self):
37
"""
38
Convert errors to primitive representation.
39
40
Returns:
41
dict: JSON-serializable error structure
42
"""
43
44
class ErrorMessage:
45
"""
46
Error message container with internationalization support.
47
48
Wraps error messages with metadata for display and localization.
49
"""
50
51
def __init__(self, message, **kwargs):
52
"""
53
Initialize error message.
54
55
Args:
56
message (str): Error message text
57
**kwargs: Additional message metadata
58
"""
59
```
60
61
### Field-Level Exceptions
62
63
Exceptions for individual field validation and conversion errors.
64
65
```python { .api }
66
class FieldError(BaseError):
67
"""
68
Base class for field-specific errors.
69
70
Associates errors with specific model fields and provides
71
context for field-level error handling.
72
"""
73
74
class ConversionError(FieldError):
75
"""
76
Raised during data type conversion failures.
77
78
Occurs when input data cannot be converted to the field's
79
expected type (e.g., string to integer conversion failure).
80
"""
81
82
def __init__(self, message, **kwargs):
83
"""
84
Initialize conversion error.
85
86
Args:
87
message (str): Conversion error description
88
**kwargs: Additional conversion context
89
"""
90
91
class ValidationError(FieldError):
92
"""
93
Raised during field validation failures.
94
95
Occurs when converted data fails field validation rules
96
(e.g., string length, numeric range, regex pattern).
97
"""
98
99
def __init__(self, message, **kwargs):
100
"""
101
Initialize validation error.
102
103
Args:
104
message (str): Validation error description
105
**kwargs: Additional validation context
106
"""
107
108
class StopValidationError(FieldError):
109
"""
110
Stops validation chain without raising an error.
111
112
Used internally to halt validation processing without
113
treating the condition as an error state.
114
"""
115
```
116
117
### Model-Level Exceptions
118
119
Exceptions for model-wide validation and data processing errors.
120
121
```python { .api }
122
class CompoundError(BaseError):
123
"""
124
Container for multiple field errors.
125
126
Aggregates validation errors from multiple fields into a
127
single exception with structured error information.
128
"""
129
130
def __init__(self, errors, **kwargs):
131
"""
132
Initialize compound error.
133
134
Args:
135
errors (dict): Field name to error mapping
136
**kwargs: Additional error context
137
"""
138
139
class DataError(BaseError):
140
"""
141
Model data errors with partial data preservation.
142
143
Raised when model validation fails but allows access to
144
partial data that was successfully processed.
145
"""
146
147
def __init__(self, errors, partial_data=None, **kwargs):
148
"""
149
Initialize data error.
150
151
Args:
152
errors: Validation errors
153
partial_data (dict, optional): Successfully processed data
154
**kwargs: Additional error context
155
"""
156
157
@property
158
def partial_data(self):
159
"""
160
Get partial data that was successfully processed.
161
162
Returns:
163
dict: Partial model data
164
"""
165
```
166
167
### Utility Exceptions
168
169
Specialized exceptions for specific error conditions.
170
171
```python { .api }
172
class MockCreationError(BaseError):
173
"""
174
Errors during mock data generation.
175
176
Raised when mock object creation fails due to field
177
constraints or generation limitations.
178
"""
179
180
class UndefinedValueError(BaseError):
181
"""
182
Raised when accessing undefined field values.
183
184
Occurs when attempting to access a field value that
185
has not been set and has no default value.
186
"""
187
188
class UnknownFieldError(BaseError):
189
"""
190
Raised when accessing nonexistent fields.
191
192
Occurs when attempting to access a field that is not
193
defined in the model schema.
194
"""
195
```
196
197
## Usage Examples
198
199
### Basic Error Handling
200
201
```python
202
from schematics.models import Model
203
from schematics.types import StringType, IntType
204
from schematics.exceptions import ValidationError, ConversionError, DataError
205
206
class Product(Model):
207
name = StringType(required=True, max_length=50)
208
price = IntType(min_value=0, required=True)
209
category = StringType(choices=['electronics', 'books', 'clothing'])
210
211
# Validation error handling
212
try:
213
product = Product({
214
'name': 'A very long product name that exceeds the maximum length limit',
215
'price': -10, # Negative price
216
'category': 'invalid_category'
217
})
218
product.validate()
219
except ValidationError as e:
220
print(f"Validation failed: {e}")
221
print(f"Error details: {e.to_primitive()}")
222
```
223
224
### Multiple Field Errors
225
226
```python
227
# DataError with multiple field validation failures
228
try:
229
invalid_product = Product({
230
'name': '', # Required field empty
231
'price': 'abc', # Cannot convert to int
232
'category': 'toys' # Not in choices
233
})
234
invalid_product.validate()
235
except DataError as e:
236
print("Multiple validation errors occurred:")
237
for field, error in e.errors.items():
238
print(f" {field}: {error}")
239
240
# Access any partial data that was successfully processed
241
if hasattr(e, 'partial_data') and e.partial_data:
242
print(f"Partial data: {e.partial_data}")
243
```
244
245
### Conversion Error Handling
246
247
```python
248
from schematics.types import DateTimeType, EmailType
249
250
class User(Model):
251
email = EmailType(required=True)
252
registered_at = DateTimeType(required=True)
253
254
try:
255
user = User({
256
'email': 'invalid-email-format',
257
'registered_at': 'not-a-date'
258
})
259
user.validate()
260
except ConversionError as e:
261
print(f"Data conversion failed: {e}")
262
except ValidationError as e:
263
print(f"Data validation failed: {e}")
264
```
265
266
### Custom Error Handling for API Responses
267
268
```python
269
def process_user_registration(user_data):
270
"""
271
Process user registration with comprehensive error handling.
272
273
Returns:
274
tuple: (success: bool, data: dict, errors: dict)
275
"""
276
try:
277
user = User(user_data)
278
user.validate()
279
return True, user.to_primitive(), None
280
281
except ValidationError as e:
282
# Single field validation error
283
return False, None, {
284
'type': 'validation_error',
285
'field': getattr(e, 'field_name', 'unknown'),
286
'message': str(e)
287
}
288
289
except DataError as e:
290
# Multiple field validation errors
291
return False, e.partial_data, {
292
'type': 'data_error',
293
'errors': e.to_primitive(),
294
'fields_with_errors': list(e.errors.keys())
295
}
296
297
except ConversionError as e:
298
# Data type conversion error
299
return False, None, {
300
'type': 'conversion_error',
301
'field': getattr(e, 'field_name', 'unknown'),
302
'message': f"Could not convert value: {str(e)}"
303
}
304
305
# Usage
306
success, data, errors = process_user_registration({
307
'email': 'john@example.com',
308
'registered_at': '2024-01-15T10:30:00Z'
309
})
310
311
if success:
312
print(f"User registered successfully: {data}")
313
else:
314
print(f"Registration failed: {errors}")
315
```
316
317
### Error Aggregation for Nested Models
318
319
```python
320
from schematics.types import ModelType, ListType
321
322
class Address(Model):
323
street = StringType(required=True)
324
city = StringType(required=True)
325
zip_code = StringType(required=True, regex=r'^\d{5}(-\d{4})?$')
326
327
class Person(Model):
328
name = StringType(required=True)
329
addresses = ListType(ModelType(Address), min_size=1)
330
331
# Complex validation with nested errors
332
try:
333
person = Person({
334
'name': '', # Invalid: required field empty
335
'addresses': [
336
{
337
'street': '123 Main St',
338
'city': 'Anytown',
339
'zip_code': 'invalid' # Invalid: doesn't match regex
340
},
341
{
342
'street': '', # Invalid: required field empty
343
'city': 'Other City',
344
'zip_code': '12345'
345
}
346
]
347
})
348
person.validate()
349
350
except DataError as e:
351
print("Nested validation errors:")
352
errors = e.to_primitive()
353
354
# Navigate nested error structure
355
if 'name' in errors:
356
print(f"Name error: {errors['name']}")
357
358
if 'addresses' in errors:
359
for i, addr_errors in enumerate(errors['addresses']):
360
if addr_errors:
361
print(f"Address {i} errors: {addr_errors}")
362
```
363
364
### Custom Exception Handling Patterns
365
366
```python
367
def safe_model_creation(model_class, data, strict=True):
368
"""
369
Safely create and validate model with configurable error handling.
370
371
Args:
372
model_class: Model class to instantiate
373
data: Raw data dictionary
374
strict: Whether to raise on validation errors
375
376
Returns:
377
tuple: (model_instance or None, error_info or None)
378
"""
379
try:
380
model = model_class(data)
381
model.validate()
382
return model, None
383
384
except ValidationError as e:
385
error_info = {
386
'error_type': 'validation',
387
'message': str(e),
388
'field': getattr(e, 'field_name', None)
389
}
390
if strict:
391
raise
392
return None, error_info
393
394
except ConversionError as e:
395
error_info = {
396
'error_type': 'conversion',
397
'message': str(e),
398
'field': getattr(e, 'field_name', None)
399
}
400
if strict:
401
raise
402
return None, error_info
403
404
except DataError as e:
405
error_info = {
406
'error_type': 'data',
407
'errors': e.to_primitive(),
408
'partial_data': getattr(e, 'partial_data', None)
409
}
410
if strict:
411
raise
412
return None, error_info
413
414
# Usage with different error handling strategies
415
model, error = safe_model_creation(User, user_data, strict=False)
416
if model:
417
print("Model created successfully")
418
else:
419
print(f"Model creation failed: {error}")
420
```