0
# Core Schema Framework
1
2
The foundational validation system that defines data structure rules, manages field requirements, handles extra key policies, and enables complex validation composition through markers and schema extension.
3
4
## Capabilities
5
6
### Schema Class
7
8
The main validation class that defines and executes validation rules against data structures.
9
10
```python { .api }
11
class Schema:
12
def __init__(self, schema, required=False, extra=PREVENT_EXTRA):
13
"""
14
Create a validation schema.
15
16
Parameters:
17
- schema: The validation rules (dict, list, callable, or value)
18
- required: Whether all keys are required by default (bool)
19
- extra: How to handle extra keys (PREVENT_EXTRA/ALLOW_EXTRA/REMOVE_EXTRA)
20
"""
21
22
def __call__(self, data):
23
"""
24
Validate data against the schema.
25
26
Parameters:
27
- data: Data to validate
28
29
Returns:
30
Validated and potentially transformed data
31
32
Raises:
33
Invalid: If validation fails
34
MultipleInvalid: If multiple validation errors occur
35
"""
36
37
def extend(self, schema, required=None, extra=None):
38
"""
39
Merge with another schema to create a new schema.
40
41
Parameters:
42
- schema: Schema to merge with
43
- required: Override required setting
44
- extra: Override extra key handling
45
46
Returns:
47
New Schema object with merged rules
48
"""
49
50
@classmethod
51
def infer(cls, data, **kwargs):
52
"""
53
Create a schema by inferring types from concrete data.
54
55
Parameters:
56
- data: Sample data to infer schema from
57
- kwargs: Additional options for schema creation
58
59
Returns:
60
New Schema object inferred from data types
61
"""
62
```
63
64
### Required Fields
65
66
Mark schema keys as required with optional default values.
67
68
```python { .api }
69
class Required:
70
def __init__(self, schema, msg=None, default=UNDEFINED, description=None):
71
"""
72
Mark a schema key as required.
73
74
Parameters:
75
- schema: The key to mark as required
76
- msg: Custom error message
77
- default: Default value or factory function
78
- description: Human-readable description
79
"""
80
```
81
82
**Usage Example:**
83
84
```python
85
from voluptuous import Schema, Required, DefaultTo
86
87
schema = Schema({
88
Required('name'): str,
89
Required('age', default=0): int,
90
Required('config', default=dict): dict,
91
})
92
93
# Validates successfully with defaults applied
94
result = schema({'name': 'John'})
95
# Result: {'name': 'John', 'age': 0, 'config': {}}
96
```
97
98
### Optional Fields
99
100
Mark schema keys as optional with optional default values.
101
102
```python { .api }
103
class Optional:
104
def __init__(self, schema, msg=None, default=UNDEFINED, description=None):
105
"""
106
Mark a schema key as optional.
107
108
Parameters:
109
- schema: The key to mark as optional
110
- msg: Custom error message
111
- default: Default value or factory function
112
- description: Human-readable description
113
"""
114
```
115
116
**Usage Example:**
117
118
```python
119
from voluptuous import Schema, Required, Optional
120
121
schema = Schema({
122
Required('name'): str,
123
Optional('nickname'): str,
124
Optional('tags', default=list): [str],
125
})
126
127
# Both work:
128
schema({'name': 'John'}) # nickname omitted
129
schema({'name': 'John', 'nickname': 'Johnny'}) # nickname provided
130
```
131
132
### Exclusive Fields
133
134
Create mutually exclusive field groups where only one field from the group can be present.
135
136
```python { .api }
137
class Exclusive:
138
def __init__(self, schema, group_of_exclusion, msg=None, description=None):
139
"""
140
Mark fields as mutually exclusive.
141
142
Parameters:
143
- schema: The key to mark as exclusive
144
- group_of_exclusion: Group identifier (string)
145
- msg: Custom error message
146
- description: Human-readable description
147
"""
148
```
149
150
**Usage Example:**
151
152
```python
153
from voluptuous import Schema, Exclusive
154
155
# Only one authentication method allowed
156
auth_schema = Schema({
157
Exclusive('username', 'auth'): str,
158
Exclusive('token', 'auth'): str,
159
Exclusive('api_key', 'auth'): str,
160
})
161
162
# Valid: only one auth method
163
auth_schema({'username': 'john'})
164
auth_schema({'token': 'abc123'})
165
166
# Invalid: multiple auth methods
167
# auth_schema({'username': 'john', 'token': 'abc123'}) # Raises ExclusiveInvalid
168
```
169
170
### Inclusive Fields
171
172
Create inclusive field groups where if any field from the group is present, all must be present.
173
174
```python { .api }
175
class Inclusive:
176
def __init__(self, schema, group_of_inclusion, msg=None, description=None, default=UNDEFINED):
177
"""
178
Mark fields as inclusive (all-or-nothing).
179
180
Parameters:
181
- schema: The key to mark as inclusive
182
- group_of_inclusion: Group identifier (string)
183
- msg: Custom error message
184
- description: Human-readable description
185
- default: Default value if group is missing
186
"""
187
```
188
189
**Usage Example:**
190
191
```python
192
from voluptuous import Schema, Inclusive, Required
193
194
# Coordinates must be provided together
195
location_schema = Schema({
196
Required('name'): str,
197
Inclusive('latitude', 'coords'): float,
198
Inclusive('longitude', 'coords'): float,
199
})
200
201
# Valid: both coords or neither
202
location_schema({'name': 'Home'}) # No coordinates
203
location_schema({'name': 'Home', 'latitude': 40.7, 'longitude': -74.0}) # Both coordinates
204
205
# Invalid: only one coordinate
206
# location_schema({'name': 'Home', 'latitude': 40.7}) # Raises InclusiveInvalid
207
```
208
209
### Remove Fields
210
211
Mark fields for removal from validated output, treating validation failures as extra keys.
212
213
```python { .api }
214
class Remove:
215
def __init__(self, schema, msg=None, description=None):
216
"""
217
Mark a field for removal from output.
218
219
Parameters:
220
- schema: The key to remove from output
221
- msg: Custom error message (rarely used)
222
- description: Human-readable description
223
"""
224
```
225
226
**Usage Example:**
227
228
```python
229
from voluptuous import Schema, Remove, Required
230
231
# Remove sensitive fields from output
232
schema = Schema({
233
Required('name'): str,
234
Required('email'): str,
235
Remove('password'): str, # Validated but not included in result
236
})
237
238
data = {'name': 'John', 'email': 'john@example.com', 'password': 'secret'}
239
result = schema(data)
240
# Result: {'name': 'John', 'email': 'john@example.com'}
241
# Password was validated but removed from output
242
```
243
244
### Object Validation
245
246
Validate object attributes instead of dictionary keys, useful for class instances.
247
248
```python { .api }
249
class Object(dict):
250
def __init__(self, schema, cls=UNDEFINED):
251
"""
252
Validate object attributes instead of dict keys.
253
254
Parameters:
255
- schema: Validation schema for object attributes
256
- cls: Expected class type (optional)
257
"""
258
```
259
260
**Usage Example:**
261
262
```python
263
from voluptuous import Schema, Object, Required
264
265
class User:
266
def __init__(self, name, age):
267
self.name = name
268
self.age = age
269
270
user_schema = Schema(Object({
271
Required('name'): str,
272
Required('age'): int,
273
}))
274
275
user = User('John', 30)
276
validated_user = user_schema(user) # Validates object attributes
277
```
278
279
### Custom Error Messages
280
281
Provide custom error messages and exception types for validation failures.
282
283
```python { .api }
284
class Msg:
285
def __init__(self, schema, msg, cls=None):
286
"""
287
Provide custom error message for validation.
288
289
Parameters:
290
- schema: The schema to validate
291
- msg: Custom error message
292
- cls: Custom exception class to raise (defaults to ValueError)
293
"""
294
```
295
296
**Usage Example:**
297
298
```python
299
from voluptuous import Schema, Msg, All, Length
300
301
password_schema = Schema(
302
Msg(All(str, Length(min=8)), 'Password must be at least 8 characters', ValueError)
303
)
304
305
try:
306
password_schema('short')
307
except ValueError as e:
308
print(e) # "Password must be at least 8 characters"
309
```
310
311
### Extra Key Handling
312
313
Control how schema validation handles keys not defined in the schema.
314
315
```python { .api }
316
PREVENT_EXTRA = 0 # Raise error for extra keys (default)
317
ALLOW_EXTRA = 1 # Include extra keys in output
318
REMOVE_EXTRA = 2 # Exclude extra keys from output
319
320
def Extra(_):
321
"""Placeholder function for allowing extra keys in schema definition."""
322
```
323
324
**Usage Example:**
325
326
```python
327
from voluptuous import Schema, Required, ALLOW_EXTRA, REMOVE_EXTRA
328
329
# Allow extra keys
330
flexible_schema = Schema({
331
Required('name'): str,
332
}, extra=ALLOW_EXTRA)
333
334
# Remove extra keys
335
strict_schema = Schema({
336
Required('name'): str,
337
}, extra=REMOVE_EXTRA)
338
339
data = {'name': 'John', 'extra_field': 'value'}
340
341
flexible_result = flexible_schema(data) # {'name': 'John', 'extra_field': 'value'}
342
strict_result = strict_schema(data) # {'name': 'John'}
343
```
344
345
### Utility Functions
346
347
Helper functions for schema creation and validation.
348
349
```python { .api }
350
def message(msg=None, cls=None):
351
"""
352
Decorator to add custom error messages to validator functions.
353
354
Parameters:
355
- msg: Custom error message
356
- cls: Custom exception class
357
"""
358
359
def validate(*a, **kw):
360
"""
361
Decorator for validating function arguments against a schema.
362
363
Parameters:
364
- *a: Positional argument schemas
365
- **kw: Keyword argument schemas
366
"""
367
368
def default_factory(value):
369
"""
370
Convert a value to a factory function for default values.
371
372
Parameters:
373
- value: Value to convert (if callable, returns as-is; otherwise wraps in lambda)
374
375
Returns:
376
Factory function that returns the value
377
"""
378
379
def raises(exc, msg=None, regex=None):
380
"""
381
Context manager for testing that an exception is raised.
382
383
Parameters:
384
- exc: Exception class that should be raised
385
- msg: Optional exact error message to match
386
- regex: Optional regex pattern to match against error message
387
388
Returns:
389
Context manager for use with 'with' statement
390
391
Raises:
392
AssertionError: If expected exception is not raised or message doesn't match
393
"""
394
```
395
396
**Message Decorator Example:**
397
398
```python
399
from voluptuous import message, Schema
400
401
@message("Value must be positive", ValueError)
402
def positive(value):
403
if value <= 0:
404
raise ValueError()
405
return value
406
407
schema = Schema(positive)
408
```
409
410
**Validate Decorator Example:**
411
412
```python
413
from voluptuous import validate, Schema, Required
414
415
@validate({'name': str, 'age': int})
416
def create_user(data):
417
return f"User: {data['name']}, Age: {data['age']}"
418
419
# Function arguments are automatically validated
420
create_user({'name': 'John', 'age': 30})
421
```
422
423
**Testing Utility Example:**
424
425
```python
426
from voluptuous import raises, Schema, Invalid, Range
427
428
# Test that validation raises expected exceptions
429
with raises(Invalid):
430
Schema(Range(min=0, max=10))(15) # Should raise Invalid
431
432
# Test with specific error message
433
with raises(Invalid, "value must be at most 10"):
434
Schema(Range(min=0, max=10))(15)
435
436
# Test with regex pattern
437
import re
438
with raises(Invalid, regex=re.compile(r"value must be at most \d+")):
439
Schema(Range(min=0, max=10))(15)
440
```