Python Cerberus
last modified October 30, 2026 Python 3.15
In this article we show how to validate data with Cerberus in Python.
Cerberus
Cerberus is a lightweight and extensible data validation library. It validates Python dictionaries, such as parsed JSON or YAML documents, form data, or configuration, against a schema.
A schema is itself a dictionary. It maps each field to a set of rules such as
type, required, min, or
regex. The schema is passed to a Validator, whose
validate method returns True or
False. The reasons for a failure are available in the
errors attribute.
The library is installed with the following command. The examples use Cerberus 1.3.
$ pip install cerberus
Types
The following is a simple demonstration of Cerberus. With the
type rule, we set the expected type of a field.
from cerberus import Validator
schema = {'name': {'type': 'string'}}
v = Validator(schema)
for document in [{'name': 'John Doe'}, {'name': 42}]:
if v.validate(document):
print('valid data')
else:
print('invalid data')
print(v.errors)
We validate a name field, which must be a string.
schema = {'name': {'type': 'string'}}
v = Validator(schema)
The schema says that the name field must be a string. A
validator can be reused for many documents.
if v.validate(document):
print('valid data')
else:
print('invalid data')
print(v.errors)
The errors attribute is a dictionary that maps field names to
lists of error messages.
$ python simple.py
Traceback (most recent call last):
File "/home/user/python/simple.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Multiple types
A list of types allows any of them. Cerberus types include
string, integer, float,
number (integer or float), boolean,
list, dict, date, and
datetime.
from cerberus import Validator
v = Validator({'words': {'type': ['string', 'list']},
'score': {'type': 'number'}})
docs = [
{'words': 'falcon', 'score': 3},
{'words': ['falcon', 'sky', 'cloud'], 'score': 4.5},
{'words': {'a': 1}, 'score': '7'},
]
for doc in docs:
print(v.validate(doc), v.errors)
The words field is either a string or a list; the
score is any number.
{'words': {'a': 1}, 'score': '7'},
Cerberus does not convert values during type checks; the string
'7' is not a number. Conversion is done with the
coerce rule shown later.
$ python types.py
Traceback (most recent call last):
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Required, empty, and nullable
By default, all fields are optional. The required rule makes a
field mandatory, empty controls whether empty strings and
collections are allowed, and nullable allows
None.
from cerberus import Validator
v = Validator()
v.schema = {
'name': {'type': 'string', 'required': True, 'empty': False},
'age': {'type': 'integer', 'nullable': True},
}
docs = [{'age': 34}, {'name': '', 'age': 34}, {'name': 'John', 'age': None}]
for doc in docs:
print(v.validate(doc), v.errors)
print(v.validate({'age': 34}, update=True), v.errors)
The name field must be present and non-empty; the
age field may be None.
print(v.validate({'age': 34}, update=True), v.errors)
With update=True, the required rule is ignored.
This is useful for partial updates, such as an HTTP PATCH request, where only
the changed fields are sent.
$ python required.py
Traceback (most recent call last):
File "/home/user/python/required.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Ranges and lengths
The min and max rules set the allowed range of
numbers. The minlength and maxlength rules limit the
length of strings and lists.
from cerberus import Validator
v = Validator()
v.schema = {
'name': {'type': 'string', 'minlength': 2, 'maxlength': 20},
'age': {'type': 'integer', 'min': 18, 'max': 65},
'tags': {'type': 'list', 'maxlength': 3},
}
doc = {'name': 'J', 'age': 4, 'tags': ['a', 'b', 'c', 'd']}
if v.validate(doc):
print('valid data')
else:
print('invalid data')
for field, errors in v.errors.items():
print(f'{field}: {errors}')
We validate a document that breaks three rules.
for field, errors in v.errors.items():
print(f'{field}: {errors}')
Cerberus does not stop at the first error; it reports all of them, which is what users of a form expect.
$ python min_max.py
Traceback (most recent call last):
File "/home/user/python/min_max.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Allowed and forbidden values
The allowed rule restricts a value to a set of options. For a
list, every element must be allowed. The forbidden rule is the
opposite.
from cerberus import Validator
v = Validator({
'role': {'type': 'string', 'allowed': ['admin', 'editor', 'viewer']},
'perms': {'type': 'list', 'allowed': ['read', 'write', 'delete']},
'user': {'type': 'string', 'forbidden': ['root', 'admin']},
})
print(v.validate({'role': 'editor', 'perms': ['read', 'write'], 'user': 'jdoe'}))
print(v.validate({'role': 'owner', 'perms': ['read', 'exec'], 'user': 'root'}))
print(v.errors)
We validate a user's role, permissions, and name.
'user': {'type': 'string', 'forbidden': ['root', 'admin']},
Reserved user names are rejected. The error message for a list names the values that are not allowed.
$ python allowed.py
Traceback (most recent call last):
File "/home/user/python/allowed.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Regular expressions and nested documents
The regex rule checks a string against a regular expression. The
schema rule of a dict field validates a nested
document.
from cerberus import Validator
v = Validator()
v.schema = {
'contact': {
'type': 'dict',
'schema': {
'phone': {'type': 'string', 'regex': r'0[0-9]{9}'},
'email': {
'type': 'string',
'required': True,
'regex': r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+',
},
},
},
}
docs = [
{'contact': {'phone': '0901123123', 'email': 'john.doe@example.com'}},
{'contact': {'phone': '0901-123-123', 'email': 'john.doe'}},
{'contact': {'phone': '0901123123'}},
]
for doc in docs:
print(v.validate(doc), v.errors)
We validate the contact details of a user.
'phone': {'type': 'string', 'regex': r'0[0-9]{9}'},
The pattern must match the whole value; Cerberus anchors it automatically. Raw strings avoid doubling the backslashes.
'contact': {
'type': 'dict',
'schema': {
...
Errors in nested documents are reported as a list containing a dictionary of the nested errors.
$ python regex.py
Traceback (most recent call last):
File "/home/user/python/regex.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Lists
For a list field, the schema rule validates every
element, and the items rule validates elements by position.
from cerberus import Validator
v = Validator({
'cities': {'type': 'list', 'schema': {'type': 'string', 'minlength': 2}},
'point': {'type': 'list', 'items': [{'type': 'number'}, {'type': 'number'}]},
'users': {
'type': 'list',
'schema': {
'type': 'dict',
'schema': {'name': {'type': 'string', 'required': True},
'age': {'type': 'integer', 'min': 0}},
},
},
})
doc = {
'cities': ['Bratislava', 'X', 'Kosice'],
'point': [1.5, 'north'],
'users': [{'name': 'John', 'age': 34}, {'age': -1}],
}
print(v.validate(doc))
for field, errors in v.errors.items():
print(field, errors)
We validate a list of strings, a fixed-length pair, and a list of dictionaries.
'point': {'type': 'list', 'items': [{'type': 'number'}, {'type': 'number'}]},
The items rule describes a tuple-like list; here a point with two
coordinates.
'users': {
'type': 'list',
'schema': {
'type': 'dict',
'schema': {...},
Each user must be a dictionary with its own schema. The errors are keyed by the index of the invalid element.
$ python lists.py
Traceback (most recent call last):
File "/home/user/python/lists.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Value coercion
The coerce rule applies a callable to a value before it is
validated. The result replaces the value in the validated document.
from datetime import datetime
from cerberus import Validator
def to_date(s):
return datetime.strptime(s, '%Y-%m-%d')
v = Validator({
'start_date': {'type': 'datetime', 'coerce': to_date},
'amount': {'type': 'integer', 'coerce': int, 'min': 1},
})
doc = {'start_date': '2026-10-30', 'amount': '25'}
print(v.validate(doc))
print(v.document)
print(doc)
print(v.validate({'start_date': '2026/10/30', 'amount': 'ten'}))
for field, errors in v.errors.items():
print(field, errors)
We convert a date string to a datetime and a numeric string to
an integer.
print(v.document) print(doc)
The validator stores the coerced values in its document
attribute; the original dictionary is not modified.
print(v.validate({'start_date': '2026/10/30', 'amount': 'ten'}))
If the callable raises an exception, Cerberus reports that the field cannot be coerced and then the type error.
$ python coercing.py
Traceback (most recent call last):
File "/home/user/python/coercing.py", line 2, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Normalization
Besides coercion, Cerberus can fill in default values, rename fields, and
remove unknown fields. The normalized method returns the
normalized document without validating it.
from cerberus import Validator
schema = {
'name': {'type': 'string', 'coerce': str.strip},
'role': {'type': 'string', 'default': 'viewer'},
'active': {'type': 'boolean', 'default': True},
'email': {'type': 'string', 'rename': 'mail'},
'mail': {'type': 'string'},
}
v = Validator(schema, purge_unknown=True)
doc = {'name': ' John Doe ', 'email': 'jdoe@example.com', 'debug': 1}
print(v.normalized(doc))
print(v.validate(doc), v.document)
We clean up a user record.
'name': {'type': 'string', 'coerce': str.strip},
'role': {'type': 'string', 'default': 'viewer'},
...
'email': {'type': 'string', 'rename': 'mail'},
Surrounding whitespace is stripped, missing fields get defaults, and the
email field is renamed to mail.
v = Validator(schema, purge_unknown=True)
With purge_unknown=True, the unknown debug field is
removed instead of causing an error.
$ python normalize.py
Traceback (most recent call last):
File "/home/user/python/normalize.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Unknown fields
By default, fields that are not in the schema are errors. The
allow_unknown option accepts them, optionally with a schema of
their own.
from cerberus import Validator
schema = {'name': {'type': 'string'}}
doc = {'name': 'John', 'nick': 'jd'}
v = Validator(schema)
print(v.validate(doc), v.errors)
v = Validator(schema, allow_unknown=True)
print(v.validate(doc), v.errors)
v = Validator(schema, allow_unknown={'type': 'integer'})
print(v.validate(doc), v.errors)
We validate the same document with three settings.
v = Validator(schema, allow_unknown={'type': 'integer'})
When allow_unknown is a rule set, unknown fields are validated
against it.
$ python unknown.py
Traceback (most recent call last):
File "/home/user/python/unknown.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Dependencies and exclusions
The dependencies rule requires other fields, optionally with
specific values. The excludes rule forbids a combination of
fields.
from cerberus import Validator
v = Validator({
'payment': {'type': 'string', 'allowed': ['card', 'transfer']},
'card_number': {'type': 'string', 'dependencies': {'payment': ['card']}},
'iban': {'type': 'string', 'dependencies': {'payment': ['transfer']},
'excludes': 'card_number'},
})
docs = [
{'payment': 'card', 'card_number': '4111111111111111'},
{'payment': 'transfer', 'card_number': '4111111111111111'},
{'payment': 'transfer', 'iban': 'SK3112000000198742637541',
'card_number': '4111111111111111'},
]
for doc in docs:
print(v.validate(doc), v.errors)
A payment either uses a card or a bank transfer.
'card_number': {'type': 'string', 'dependencies': {'payment': ['card']}},
A card number is allowed only when the payment method is
card.
'iban': {'type': 'string', 'dependencies': {'payment': ['transfer']},
'excludes': 'card_number'},
An IBAN requires the transfer method and must not be given
together with a card number.
$ python dependencies.py
Traceback (most recent call last):
File "/home/user/python/dependencies.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Custom checks
The check_with rule calls a function that reports errors through
the error callback.
from cerberus import Validator
def luhn(field, value, error):
if not value.isdigit():
return
digits = [int(d) for d in value]
checksum = sum(digits[-1::-2])
checksum += sum(sum(divmod(2 * d, 10)) for d in digits[-2::-2])
if checksum % 10 != 0:
error(field, 'invalid checksum')
v = Validator({'card': {'type': 'string', 'regex': r'\d{13,19}',
'check_with': luhn}})
for card in ['4111111111111111', '4111111111111112', '4111-1111']:
print(card, v.validate({'card': card}), v.errors)
We validate credit card numbers with the Luhn checksum.
def luhn(field, value, error):
if not value.isdigit():
return
All rules of a field run even if an earlier one failed, so the function must not assume that the regex rule has passed.
if checksum % 10 != 0:
error(field, 'invalid checksum')
The function returns nothing; it reports a problem by calling
error with the field name and a message.
$ python check_with.py
Traceback (most recent call last):
File "/home/user/python/check_with.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Custom rules
New rules are added by subclassing Validator. A method named
_validate_<rule> implements a rule, and a method named
_check_with_<name> can be referenced by name in
check_with.
from cerberus import Validator
class AppValidator(Validator):
def _validate_is_odd(self, constraint, field, value):
"""{'type': 'boolean'}"""
if constraint and value % 2 == 0:
self._error(field, 'must be an odd number')
def _check_with_title_case(self, field, value):
if value != value.title():
self._error(field, 'must be in title case')
schema = {
'count': {'type': 'integer', 'is_odd': True},
'city': {'type': 'string', 'check_with': 'title_case'},
}
v = AppValidator(schema)
print(v.validate({'count': 7, 'city': 'New York'}), v.errors)
print(v.validate({'count': 8, 'city': 'new york'}), v.errors)
We add an is_odd rule and a title_case check.
def _validate_is_odd(self, constraint, field, value):
"""{'type': 'boolean'}"""
The docstring is required: it is a schema for the rule's own constraint, which Cerberus uses to validate schemas. Here the constraint must be a boolean.
'city': {'type': 'string', 'check_with': 'title_case'},
A string in check_with refers to the
_check_with_title_case method.
$ python custom_rule.py
Traceback (most recent call last):
File "/home/user/python/custom_rule.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Validating objects
Cerberus validates mappings. To validate an object, we convert it to a dictionary first.
from dataclasses import dataclass, asdict
from cerberus import Validator
@dataclass
class Person:
name: str
age: int
class PersonValidator(Validator):
def validate_person(self, person):
return self.validate(asdict(person))
schema = {'name': {'type': 'string', 'minlength': 2},
'age': {'type': 'integer', 'min': 18, 'max': 65}}
v = PersonValidator(schema)
for p in [Person('John Doe', 34), Person('Lucy', 2)]:
if v.validate_person(p):
print(f'{p}: valid')
else:
print(f'{p}: {v.errors}')
We validate Person dataclass instances.
def validate_person(self, person):
return self.validate(asdict(person))
The asdict function converts a dataclass instance into a
dictionary, including nested dataclasses.
$ python dataclass_validator.py
Traceback (most recent call last):
File "/home/user/python/dataclass_validator.py", line 1, in <module>
from dataclasses import dataclass, asdict
File "/usr/lib/python3.15/dataclasses.py", line 2, in <module>
import types
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
ImportError: cannot import name 'FunctionType' from 'types' (consider renaming '/home/user/python/types.py' since it has the same name as the standard library module named 'types' and prevents importing that standard library module)
Validating a YAML file
Data loaded from YAML or JSON files are dictionaries and lists, which is exactly what Cerberus validates.
cities: - Bratislava - Kosice - Trnava - Moldava - Trencin population: 1234567
The file contains a list of cities and a number.
import yaml
from cerberus import Validator
v = Validator({
'cities': {'type': 'list', 'schema': {'type': 'string'}},
'population': {'type': 'integer', 'min': 0},
})
with open('cities.yaml') as f:
data = yaml.safe_load(f)
print(data)
if v.validate(data):
print('valid data')
else:
print('invalid data')
print(v.errors)
We load the file with PyYAML and validate the result.
data = yaml.safe_load(f)
The safe_load function creates only basic Python types and is
safe to use with untrusted files.
$ python from_yaml.py
Traceback (most recent call last):
File "/home/user/python/from_yaml.py", line 1, in <module>
import yaml
File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/__init__.py", line 8, in <module>
from .loader import *
File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/loader.py", line 4, in <module>
from .reader import *
File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/reader.py", line 22, in <module>
import codecs, re
File "/usr/lib/python3.15/re/__init__.py", line 126, in <module>
import enum
File "/usr/lib/python3.15/enum.py", line 3, in <module>
from types import MappingProxyType, DynamicClassAttribute
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
ImportError: cannot import name 'FunctionType' from 'types' (consider renaming '/home/user/python/types.py' since it has the same name as the standard library module named 'types' and prevents importing that standard library module)
Schema errors
Cerberus validates the schema itself when the validator is created. A typo in a rule name is caught immediately.
from cerberus import Validator
from cerberus.schema import SchemaError
try:
Validator({'age': {'type': 'integer', 'minimum': 18}})
except SchemaError as e:
print(e)
The rule is called min, not minimum. Creating the
validator raises a SchemaError.
$ python schema_error.py
Traceback (most recent call last):
File "/home/user/python/schema_error.py", line 1, in <module>
from cerberus import Validator
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
from cerberus.platform import importlib_metadata
File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
import importlib.metadata as importlib_metadata
File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
import functools
File "/usr/lib/python3.15/functools.py", line 22, in <module>
from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
File "/home/user/python/types.py", line 1, in <module>
from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)
Source
In this article we have shown how to validate data in Python with Cerberus.
Author
List all Python tutorials.